home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.004 / xemacs-1 / xemacs-19.13 / man / makeinfo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-08  |  219.4 KB  |  9,108 lines

  1. /* Makeinfo -- convert texinfo format files into info files.
  2.  
  3.    Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
  4.    Copyright (C) 1994, 1995 Sun Microsystems.
  5.    Copyright (C) 1995 Amdahl Corporation.
  6.  
  7.    This file is part of GNU Info.
  8.  
  9.    Makeinfo is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY.  No author or distributor accepts
  11.    responsibility to anyone for the consequences of using it or for
  12.    whether it serves any particular purpose or works at all, unless he
  13.    says so in writing.  Refer to the GNU Emacs General Public License
  14.    for full details.
  15.  
  16.    Everyone is granted permission to copy, modify and redistribute
  17.    Makeinfo, but only under the conditions described in the GNU Emacs
  18.    General Public License.   A copy of this license is supposed to
  19.    have been given to you along with GNU Emacs so you can know your
  20.    rights and responsibilities.  It should be in a file named COPYING.
  21.    Among other things, the copyright notice and this notice must be
  22.    preserved on all copies.  */
  23.  
  24. /* This is Makeinfo version 1.63.  If you change the version number of
  25.    Makeinfo, please change it here and at the lines reading:
  26.  
  27.     int major_version = 1;
  28.     int minor_version = 63;
  29.  
  30.    in the code below.
  31.  
  32.    Makeinfo is authored by Brian Fox (bfox@ai.mit.edu).
  33.    March 1994: additions by Ben Wing (wing@netcom.com).
  34. */
  35.  
  36. #if defined (HAVE_CONFIG_H)
  37. #include <config.h>
  38. #endif /* HAVE_CONFIG_H */
  39.  
  40. /* You can change some of the behaviour of Makeinfo by changing the
  41.    following defines: */
  42.  
  43. /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
  44.    appear within an @table, @ftable, or @itemize environment to have
  45.    standard paragraph indentation.  Without this, such paragraphs have
  46.    no starting indentation. */
  47. /* #define INDENT_PARAGRAPHS_IN_TABLE */
  48.  
  49. /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
  50.    that @example should increase indentation by.  This incremement is used
  51.    for all insertions which indent the enclosed text. */
  52. #define DEFAULT_INDENTATION_INCREMENT 5
  53.  
  54. /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
  55.    the first lines of paragraphs receive by default, where no other
  56.    value has been specified.  Users can change this value on the command
  57.    line, with the --paragraph-indent option, or within the texinfo file,
  58.    with the @paragraphindent command. */
  59. #define PARAGRAPH_START_INDENT 3
  60.  
  61. /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
  62.    wish to appear between paragraphs.  A value of 1 creates a single blank
  63.    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
  64.    newlines in the input file (i.e., one or more blank lines). */
  65. #define DEFAULT_PARAGRAPH_SPACING 1
  66.  
  67. /* Define HAVE_MACROS to enable the macro facility of TeXinfo.  Using this
  68.    facility, users can create their own command procedures with arguments. */
  69. #define HAVE_MACROS
  70.  
  71. /* **************************************************************** */
  72. /*                                    */
  73. /*            Include File Declarations               */
  74. /*                                    */
  75. /* **************************************************************** */
  76.  
  77. /* Indent #pragma so that older Cpp's don't try to parse it. */
  78. #if defined (_AIX)
  79.  # pragma alloca
  80. #endif /* _AIX */
  81.  
  82. #include <stdio.h>
  83. #include <unistd.h>
  84. #include <sys/types.h>
  85. #include <ctype.h>
  86. #include <sys/stat.h>
  87. #include <pwd.h>
  88. #include <errno.h>
  89.  
  90. #include <stdarg.h>
  91. #include "getopt.h"
  92. #if defined (HAVE_UNISTD_H)
  93. #include <unistd.h>
  94. #endif /* HAVE_UNISTD_H */
  95.  
  96. #if defined (VMS)
  97. #include <perror.h>
  98. #endif
  99.  
  100. #include <stdlib.h>
  101.  
  102. #if defined (HAVE_STRING_H)
  103. #include <string.h>
  104. #define HAVE_STRDUP
  105. #define HAVE_STRCASECMP
  106. #else
  107. #include <strings.h>
  108. #endif /* !HAVE_STRING_H */
  109.  
  110. #if defined (TM_IN_SYS_TIME)
  111. #include <sys/time.h>
  112. #else
  113. #include <time.h>
  114. #endif /* !TM_IN_SYS_TIME */
  115.  
  116. #if defined (HAVE_SYS_FCNTL_H)
  117. #include <sys/fcntl.h>
  118. #else
  119. #include <fcntl.h>
  120. #endif /* !HAVE_SYS_FCNTL_H */
  121.  
  122. #if defined (HAVE_SYS_FILE_H)
  123. #include <sys/file.h>
  124. #endif /* HAVE_SYS_FILE_H */
  125.  
  126. #if defined (__GNUC__) && !defined (alloca)
  127. #define alloca __builtin_alloca
  128. #else
  129. #if defined(HAVE_ALLOCA_H)
  130. #include <alloca.h>
  131. #else /* !HAVE_ALLOCA_H */
  132. #if !defined (_AIX)
  133. extern char *alloca ();
  134. #endif /* !_AIX */
  135. #endif /* !HAVE_ALLOCA_H */
  136. #endif /* !__GNUC__ */
  137.  
  138. #ifdef __SUNPRO_C
  139. void *__builtin_alloca (unsigned int);
  140. #endif
  141.  
  142. static void *xmalloc (unsigned int), *xrealloc (void *, unsigned int);
  143. #if defined (__osf__)
  144. extern void *malloc (), *realloc ();
  145. #endif /* __osf__ */
  146.  
  147. static char **get_brace_args (int);
  148. static int array_len (char **);
  149. static void free_array (char **);
  150. static void isolate_nodename (char *);
  151.  
  152. /* Non-zero means that we are currently hacking the insides of an
  153.    insertion which would use a fixed width font. */
  154. static int in_fixed_width_font = 0;
  155.  
  156. /* Non-zero means that start_paragraph () MUST be called before we pay
  157.    any attention to close_paragraph () calls. */
  158. int must_start_paragraph = 0;
  159.  
  160. /* Non-zero means a string is in execution, as opposed to a file. */
  161. static int executing_string = 0;
  162.  
  163. #if defined (HAVE_MACROS)
  164. /* If non-NULL, this is an output stream to write the full macro expansion
  165.    of the input text to.  The resultant file is another texinfo file, but
  166.    missing @include, @infoinclude, @macro, and macro invocations.  Instead,
  167.    all of the text is placed within the file. */
  168. FILE *macro_expansion_output_stream = (FILE *)NULL;
  169.  
  170. /* Here is a structure used to remember input text strings and offsets
  171.    within them. */
  172. typedef struct {
  173.   char *pointer;        /* Pointer to the input text. */
  174.   int offset;            /* Offset of the last character output. */
  175. } ITEXT;
  176.  
  177. static ITEXT **itext_info = (ITEXT **)NULL;
  178. static int itext_size = 0;
  179.  
  180. /* Non-zero means to inhibit the writing of macro expansions to the output
  181.    stream.  This is used in special cases where the output has already been
  182.    written. */
  183. int me_inhibit_expansion = 0;
  184.  
  185. static ITEXT *remember_itext (char *, int);
  186. static void forget_itext (char *);
  187. static void me_append_before_this_command (void);
  188. static void append_to_expansion_output (int);
  189. static void write_region_to_macro_output (char *, int, int);
  190. static void maybe_write_itext (char *, int);
  191. static void me_execute_string (char *);
  192. #endif /* HAVE_MACROS */
  193.  
  194. /* Some systems don't declare this function in pwd.h. */
  195. struct passwd *getpwnam ();
  196.  
  197.  
  198. /* **************************************************************** */
  199. /*                                    */
  200. /*                  Global Defines                  */
  201. /*                                    */
  202. /* **************************************************************** */
  203.  
  204. /* Error levels */
  205. #define NO_ERROR 0
  206. #define SYNTAX     2
  207. #define FATAL     4
  208.  
  209. /* C's standard macros don't check to make sure that the characters being
  210.    changed are within range.  So I have to check explicitly. */
  211.  
  212. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  213.    have to do it. */
  214. #ifndef toupper
  215. #define toupper(c) ((c) - 32)
  216. #endif
  217.  
  218. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  219. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  220.  
  221. #define control_character_bit 0x40 /* %01000000, must be off. */
  222. #define meta_character_bit 0x080/* %10000000, must be on.  */
  223. #define CTL(c) ((c) & (~control_character_bit))
  224. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  225. #define META(c) ((c) | (meta_character_bit))
  226. #define UNMETA(c) ((c) & (~meta_character_bit))
  227.  
  228. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  229. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  230. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  231.  
  232. #ifndef isletter
  233. #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
  234. #endif
  235.  
  236. #ifndef isupper
  237. #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
  238. #endif
  239.  
  240. #ifndef isdigit
  241. #define isdigit(c)  ((c) >= '0' && (c) <= '9')
  242. #endif
  243.  
  244. #ifndef digit_value
  245. #define digit_value(c) ((c) - '0')
  246. #endif
  247.  
  248. #define member(c, s) (strchr (s, c) != NULL)
  249.  
  250. #define COMMAND_PREFIX '@'
  251.  
  252. /* Stuff for splitting large files. */
  253. #define SPLIT_SIZE_THRESHOLD 70000  /* What's good enough for Stallman... */
  254. #define DEFAULT_SPLIT_SIZE 50000    /* Is probably good enough for me. */
  255. int splitting = 1;            /* Always true for now. */
  256.  
  257. typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */
  258.  
  259.  
  260. /* **************************************************************** */
  261. /*                                    */
  262. /*                Global Variables                */
  263. /*                                    */
  264. /* **************************************************************** */
  265.  
  266. /* Global pointer to argv[0]. */
  267. char *progname;
  268.  
  269. /* The current input file state. */
  270. char *input_filename;
  271. char *input_text;
  272. int size_of_input_text;
  273. int input_text_offset;
  274. int line_number;
  275.  
  276. #define curchar() input_text[input_text_offset]
  277.  
  278. #define command_char(c) ((!whitespace(c)) && \
  279.              ((c) != '\n') && \
  280.              ((c) != '{') && \
  281.              ((c) != '}') && \
  282.              ((c) != '='))
  283.  
  284. #define skip_whitespace() while (input_text_offset != size_of_input_text \
  285.                  && whitespace(curchar()))\
  286.   input_text_offset++
  287.  
  288. #define skip_whitespace_and_newlines() \
  289.   do { \
  290.    while (input_text_offset != size_of_input_text \
  291.       && (whitespace (curchar ()) \
  292.           || (curchar () == '\n'))) \
  293.       { \
  294.      if (curchar () == '\n') \
  295.        line_number++; \
  296.      input_text_offset++; \
  297.       } \
  298.    } while (0)
  299.  
  300. /* Return non-zero if STRING is the text at input_text + input_text_offset,
  301.    else zero. */
  302. #define looking_at(string) \
  303.   (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
  304.  
  305. /* And writing to the output. */
  306.  
  307. /* The output file name. */
  308. char *output_filename = (char *)NULL;
  309. char *pretty_output_filename;
  310.  
  311. /* Name of the output file that the user elected to pass on the command line.
  312.    Such a name overrides any name found with the @setfilename command. */
  313. char *command_output_filename = (char *)NULL;
  314.  
  315. /* A colon separated list of directories to search for files included
  316.    with @include.  This can be controlled with the `-I' option to makeinfo. */
  317. char *include_files_path = (char *)NULL;
  318.  
  319. /* Current output stream. */
  320. FILE *output_stream;
  321.  
  322. /* Position in the output file. */
  323. int output_position;
  324.  
  325. /* Output paragraph buffer. */
  326. unsigned char *output_paragraph;
  327.  
  328. /* Offset into OUTPUT_PARAGRAPH. */
  329. int output_paragraph_offset;
  330.  
  331. /* The output paragraph "cursor" horizontal position. */
  332. int output_column = 0;
  333.  
  334. /* Non-zero means output_paragraph contains text. */
  335. int paragraph_is_open = 0;
  336.  
  337. #define INITIAL_PARAGRAPH_SPACE 5000
  338. int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  339.  
  340. /* Filling.. */
  341. /* Non-zero indicates that filling will take place on long lines. */
  342. int filling_enabled = 1;
  343.  
  344. /* Non-zero means that words are not to be split, even in long lines.  This
  345.    gets changed for cm_w (). */
  346. int non_splitting_words = 0;
  347.  
  348. /* Non-zero indicates that filling a line also indents the new line. */
  349. int indented_fill = 0;
  350.  
  351. /* The column at which long lines are broken. */
  352. int fill_column = 72;
  353.  
  354. /* The amount of indentation to apply at the start of each line. */
  355. int current_indent = 0;
  356.  
  357. /* The amount of indentation to add at the starts of paragraphs.
  358.    0 means don't change existing indentation at paragraph starts.
  359.    > 0 is amount to indent new paragraphs by.
  360.    < 0 means indent to column zero by removing indentation if necessary.
  361.  
  362.    This is normally zero, but some people prefer paragraph starts to be
  363.    somewhat more indented than paragraph bodies.  A pretty value for
  364.    this is 3. */
  365. int paragraph_start_indent = PARAGRAPH_START_INDENT;
  366.  
  367. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  368.    @example uses this to line up the left columns of the example text.
  369.    A negative value for this variable is incremented each time it is used.
  370.    @noindent uses this to inhibit indentation for a single paragraph.  */
  371. int inhibit_paragraph_indentation = 0;
  372.  
  373. /* Indentation that is pending insertion.  We have this for hacking lines
  374.    which look blank, but contain whitespace.  We want to treat those as
  375.    blank lines. */
  376. int pending_indent = 0;
  377.  
  378. /* The amount that indentation increases/decreases by. */
  379. int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
  380.  
  381. /* Non-zero indicates that indentation is temporarily turned off. */
  382. int no_indent = 1;
  383.  
  384. /* Non-zero means forcing output text to be flushright. */
  385. int force_flush_right = 0;
  386.  
  387. /* Non-zero means that the footnote style for this document was set on
  388.    the command line, which overrides any other settings. */
  389. int footnote_style_preset = 0;
  390.  
  391. /* Non-zero means that we automatically number footnotes that have no
  392.    specified marker. */
  393. int number_footnotes = 1;
  394.  
  395. /* The current footnote number in this node.  Each time a new node is
  396.    started this is reset to 1. */
  397. int current_footnote_number = 1;
  398.  
  399. /* Command name in the process of being hacked. */
  400. char *command;
  401.  
  402. /* The index in our internal command table of the currently
  403.    executing command. */
  404. int command_index;
  405.  
  406. /* A search string which is used to find a line defining a node. */
  407. char node_search_string[] =
  408.   { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', '\0' };
  409.  
  410. /* A search string which is used to find a line defining a menu. */
  411. char menu_search_string[] =
  412.   { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', '\0' };
  413.  
  414. /* A search string which is used to find the first @setfilename. */
  415. char setfilename_search[] =
  416.   { COMMAND_PREFIX,
  417.       's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '\0' };
  418.  
  419. /* A stack of file information records.  If a new file is read in with
  420.    "@input", we remember the old input file state on this stack. */
  421. typedef struct fstack
  422. {
  423.   struct fstack *next;
  424.   char *filename;
  425.   char *text;
  426.   int size;
  427.   int offset;
  428.   int line_number;
  429. } FSTACK;
  430.  
  431. FSTACK *filestack = (FSTACK *) NULL;
  432.  
  433. /* Stuff for nodes. */
  434. /* The current nodes node name. */
  435. char *current_node = (char *)NULL;
  436.  
  437. /* The current nodes section level. */
  438. int current_section = 0;
  439.  
  440. /* The filename of the current input file.  This is never freed. */
  441. char *node_filename = (char *)NULL;
  442.  
  443. /* What we remember for each node. */
  444. typedef struct tentry
  445. {
  446.   struct tentry *next_ent;
  447.   char *node;        /* name of this node. */
  448.   char *prev;        /* name of "Prev:" for this node. */
  449.   char *next;        /* name of "Next:" for this node. */
  450.   char *up;        /* name of "Up:" for this node.   */
  451.   int position;        /* output file position of this node. */
  452.   int line_no;        /* defining line in source file. */
  453.   char *filename;    /* The file that this node was found in. */
  454.   int touched;        /* non-zero means this node has been referenced. */
  455.   int flags;        /* Room for growth.  Right now, contains 1 bit. */
  456. } TAG_ENTRY;
  457.  
  458. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  459.    we turn on this flag bit in node-b's tag entry.  This means that when
  460.    it is time to validate node-b, we don't report an additional error
  461.    if there was no "Prev" field. */
  462. #define PREV_ERROR 0x1
  463. #define NEXT_ERROR 0x2
  464. #define UP_ERROR   0x4
  465. #define NO_WARN       0x8
  466. #define IS_TOP        0x10
  467.  
  468. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  469.  
  470. #if defined (HAVE_MACROS)
  471. #define ME_RECURSE    0x01
  472. #define ME_QUOTE_ARG    0x02
  473.  
  474. /* Macro definitions for user-defined commands. */
  475. typedef struct {
  476.   char *name;            /* Name of the macro. */
  477.   char **arglist;        /* Args to replace when executing. */
  478.   char *body;            /* Macro body. */
  479.   char *source_file;        /* File where this macro is defined. */
  480.   int source_lineno;        /* Line number within FILENAME. */
  481.   int inhibited;        /* Non-zero means make find_macro () fail. */
  482.   int flags;            /* ME_RECURSE, ME_QUOTE_ARG, etc. */
  483. } MACRO_DEF;
  484.  
  485. static void add_macro (char *name, char **arglist, char *body,
  486.                char *source_file, int source_lineno, int flags);
  487. static void execute_macro (MACRO_DEF *);
  488. static MACRO_DEF *find_macro (char *), *delete_macro (char *);
  489. #endif /* HAVE_MACROS */
  490.  
  491. /* Menu reference, *note reference, and validation hacking. */
  492.  
  493. /* The various references that we know about. */
  494. enum reftype
  495. {
  496.   menu_reference, followed_reference
  497. };
  498.  
  499. /* A structure to remember references with.  A reference to a node is
  500.    either an entry in a menu, or a cross-reference made with [px]ref. */
  501. typedef struct node_ref
  502. {
  503.   struct node_ref *next;
  504.   char *node;            /* Name of node referred to. */
  505.   char *containing_node;    /* Name of node containing this reference. */
  506.   int line_no;            /* Line number where the reference occurs. */
  507.   int section;            /* Section level where the reference occurs. */
  508.   char *filename;        /* Name of file where the reference occurs. */
  509.   enum reftype type;        /* Type of reference, either menu or note. */
  510. } NODE_REF;
  511.  
  512. /* The linked list of such structures. */
  513. NODE_REF *node_references = (NODE_REF *) NULL;
  514.  
  515. /* Flag which tells us whether to examine menu lines or not. */
  516. int in_menu = 0;
  517.  
  518. /* Non-zero means that we have seen "@top" once already. */
  519. int top_node_seen = 0;
  520.  
  521. /* Non-zero means that we have seen a non-"@top" node already. */
  522. int non_top_node_seen = 0;
  523.  
  524. /* Flags controlling the operation of the program. */
  525.  
  526. /* Default is to notify users of bad choices. */
  527. int print_warnings = 1;
  528.  
  529. /* Default is to check node references. */
  530. int validating = 1;
  531.  
  532. /* Non-zero means do not output "Node: Foo" for node separations. */
  533. int no_headers = 0;
  534.  
  535. /* Number of errors that we tolerate on a given fileset. */
  536. int max_error_level = 100;
  537.  
  538. /* Maximum number of references to a single node before complaining. */
  539. int reference_warning_limit = 1000;
  540.  
  541. /* Non-zero means print out information about what is going on when it
  542.    is going on. */
  543. int verbose_mode = 0;
  544.  
  545. /* Non-zero means to be relaxed about the input file.  This is useful when
  546.    we can successfully format the input, but it doesn't strictly match our
  547.    somewhat pedantic ideas of correctness.  Right now, it affects what
  548.    @table and @itemize do without arguments. */
  549. int allow_lax_format = 0;
  550.  
  551. /* Count of @ifinfo commands seen.  @ifinfo is handled specially
  552.    to allow non-hierarchical contstructions like
  553.  
  554.    @ifinfo
  555.    @table foo
  556.    @end ifinfo
  557. */
  558. int ifinfo_count = 0;
  559.  
  560. /* The list of commands that we hack in texinfo.  Each one
  561.    has an associated function.  When the command is encountered in the
  562.    text, the associated function is called with START as the argument.
  563.    If the function expects arguments in braces, it remembers itself on
  564.    the stack.  When the corresponding close brace is encountered, the
  565.    function is called with END as the argument. */
  566.  
  567. #define START 0
  568. #define END 1
  569.  
  570. typedef struct brace_element
  571. {
  572.   struct brace_element *next;
  573.   COMMAND_FUNCTION *proc;
  574.   int pos, line;
  575. } BRACE_ELEMENT;
  576.  
  577. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  578.  
  579. /* Forward declarations. */
  580. #if !defined (HAVE_STRDUP)
  581. extern char *strdup ();
  582. #endif /* HAVE_STRDUP */
  583.  
  584. static void convert_from_stream (FILE *, char *);
  585. static void convert_from_file (char *);
  586. static void top_defindex (char *, int);
  587.  
  588. static void insert_self (), cm_ignore_line (void);
  589.  
  590. static void
  591.   cm_asterisk (void), cm_dots (int), cm_bullet (int), cm_TeX (int),
  592.   cm_copyright (int), cm_code (int), cm_samp (int), cm_file (int),
  593.   cm_kbd (int), cm_key (int), cm_ctrl (int, int, int), cm_var (int, int, int),
  594.   cm_dfn (int, int), cm_emph (int), cm_strong (int, int), cm_cite (int, int),
  595.   cm_italic (int, int, int), cm_bold (int, int, int), cm_roman (int, int, int),
  596.   cm_title (int, int, int), cm_w (int, int, int), cm_refill (void),
  597.   cm_titlefont (int, int, int);
  598.  
  599. static void
  600.   cm_chapter (void), cm_unnumbered (void), cm_appendix (void), cm_top (void),
  601.   cm_section (void), cm_unnumberedsec (void), cm_appendixsec (void),
  602.   cm_subsection (void), cm_unnumberedsubsec (void), cm_appendixsubsec (void),
  603.   cm_subsubsection (void), cm_unnumberedsubsubsec (void),
  604.   cm_appendixsubsubsec (void), cm_heading (void), cm_chapheading (void),
  605.   cm_subheading (void), cm_subsubheading (void), cm_majorheading (void),
  606.   cm_raisesections (void), cm_lowersections (void);
  607.  
  608. /* All @defxxx commands map to cm_defun (). */
  609. static void cm_defun (void);
  610.  
  611. static void
  612.   cm_node (void), cm_menu (void), cm_xref (int), cm_ftable (void),
  613.   cm_vtable (void), cm_pxref (int), cm_inforef (int), cm_quotation (void),
  614.   cm_display (void), cm_itemize (void), cm_enumerate (void), cm_table (void),
  615.   cm_itemx (void), cm_noindent (void), cm_setfilename (void), cm_br (void),
  616.   cm_sp (void), cm_group (void), cm_center (void), cm_include (void),
  617.   cm_bye (void), cm_item (void), cm_end (void), cm_infoinclude (void),
  618.   cm_ifinfo (void), cm_kindex (void), cm_cindex (void), cm_findex (void),
  619.   cm_pindex (void), cm_vindex (void), cm_tindex (void), cm_asis (void),
  620.   cm_synindex (void), cm_printindex (void), cm_minus (int),
  621.   cm_footnote (void), cm_force_abbreviated_whitespace (void),
  622.   cm_example (void), cm_smallexample (void), cm_lisp (void), cm_format (void),
  623.   cm_exdent (void), cm_defindex (void), cm_defcodeindex (void),
  624.   cm_sc (int, int, int), cm_result (int), cm_expansion (int), cm_equiv (int),
  625.   cm_print (int), cm_error (int), cm_point (int), cm_today (int),
  626.   cm_flushleft (void), cm_flushright (void), cm_smalllisp (void),
  627.   cm_math (void), cm_cartouche (void), cm_ignore_sentence_ender (void);
  628.  
  629. /* Conditionals. */
  630. static void cm_set (void), cm_clear (void), cm_ifset (void), cm_ifclear (void);
  631. static void cm_value (int, int, int), cm_ifeq (void);
  632.  
  633. #if defined (HAVE_MACROS)
  634. /* Define a user-defined command which is simple substitution. */
  635. static void cm_macro (void), cm_unmacro (void);
  636. #endif /* HAVE_MACROS */
  637.  
  638. /* Options. */
  639. static void cm_paragraphindent (void), cm_footnotestyle (void);
  640.  
  641. /* Internals. */
  642. static void do_nothing (void);
  643. static void command_name_condition (void);
  644. static void insert_self (void);
  645. static void misplaced_brace (void);
  646. static void cm_obsolete (int arg, int start, int end);
  647.  
  648. static int error (char *, ...);
  649. static int line_error (char *, ...);
  650. static int warning (char *, ...);
  651. static void add_word_args (char *, ...);
  652. static void execute_string (char *, ...);
  653.  
  654. static void print_version_info (void);
  655. static void memory_error (char *callers_name, int bytes_wanted);
  656. static void usage (FILE *stream, int exit_value);
  657. static void push_node_filename (void);
  658. static void pop_node_filename (void);
  659. static void remember_error (void);
  660. static void init_internals (void);
  661. static void init_paragraph (void);
  662. static void reader_loop (void);
  663. static void read_command (void);
  664. static void init_brace_stack (void);
  665. static void remember_brace (COMMAND_FUNCTION *proc);
  666. static void remember_brace_1 (COMMAND_FUNCTION *proc, int position);
  667. static void discard_braces (void);
  668. static void add_word (char *string);
  669. static void add_char (int character);
  670. static void insert (int character);
  671. static void flush_output (void);
  672. static void close_paragraph_with_lines (int lines);
  673. static void close_paragraph (void);
  674. static void ignore_blank_line (void);
  675. static void do_flush_right_indentation (void);
  676. static void start_paragraph (void);
  677. static void indent (int amount);
  678. static void init_insertion_stack (void);
  679. static void init_tag_table (void);
  680. static void write_tag_table (void);
  681. static void write_tag_table_internal (int indirect_p);
  682. static void normalize_node_name (char *string);
  683. static void validate_file (TAG_ENTRY *tag_table);
  684. static void split_file (char *filename, int size);
  685. static void validate_other_references (register NODE_REF *ref_list);
  686. static NODE_REF *find_node_reference (char *node, register NODE_REF *ref_list);
  687. static void do_enumeration (int type, char *default_string);
  688. static void handle_variable (int action);
  689. static void handle_variable_internal (int action, char *name);
  690. static void init_indices (void);
  691. static void undefindex (char *name);
  692. static void defindex (char *name, int code);
  693. static void gen_defindex (int code);
  694. static void define_user_command (char *name, COMMAND_FUNCTION *proc,
  695.                  int needs_braces_p);
  696. static void convert_from_loaded_file (char *name);
  697. static void free_pending_notes (void);
  698. static void output_pending_notes (void);
  699. static void free_node_references (void);
  700. static int set_paragraph_indent (char *string);
  701. static int set_footnote_style (char *string);
  702. static int self_delimiting (int character);
  703. static int search_forward (char *string, int from);
  704. static int validate (char *tag, int line, char *label);
  705. static void pop_and_call_brace (void);
  706. static char *expand_filename (char *filename, char *input_name);
  707. static char *filename_part (char *filename);
  708. static char *full_pathname (char *filename);
  709. static char *get_file_info_in_path (char *filename, char *path,
  710.                     struct stat *finfo);
  711. static char *glean_node_from_menu (int remember_reference);
  712.  
  713. typedef struct
  714. {
  715.   char *name;
  716.   COMMAND_FUNCTION *proc;
  717.   int argument_in_braces;
  718. } COMMAND;
  719.  
  720. /* Stuff for defining commands on the fly. */
  721. COMMAND **user_command_array = (COMMAND **) NULL;
  722. int user_command_array_len = 0;
  723.  
  724. #define NO_BRACE_ARGS 0
  725. #define BRACE_ARGS 1
  726.  
  727. static COMMAND CommandTable[] = {
  728.   { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  729.   { "'", insert_self, NO_BRACE_ARGS },
  730.   { "*", cm_asterisk, NO_BRACE_ARGS },
  731.   { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  732.   { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
  733.   { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  734.   { "|", do_nothing, NO_BRACE_ARGS },
  735.   { "@", insert_self, NO_BRACE_ARGS },
  736.   { " ", insert_self, NO_BRACE_ARGS },
  737.   { "\n", insert_self, NO_BRACE_ARGS },
  738.   { "TeX", cm_TeX, BRACE_ARGS },
  739.   { "`", insert_self, NO_BRACE_ARGS },
  740.   { "appendix", cm_appendix, NO_BRACE_ARGS },
  741.   { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
  742.   { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
  743.   { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  744.   { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  745.   { "asis", cm_asis, BRACE_ARGS },
  746.   { "b", cm_bold, BRACE_ARGS },
  747.   { "br", cm_br, NO_BRACE_ARGS },
  748.   { "bullet", cm_bullet, BRACE_ARGS },
  749.   { "bye", cm_bye, NO_BRACE_ARGS },
  750.   { "c", cm_ignore_line, NO_BRACE_ARGS },
  751.   { "cartouche", cm_cartouche, NO_BRACE_ARGS },
  752.   { "center", cm_center, NO_BRACE_ARGS },
  753.   { "chapheading", cm_chapheading, NO_BRACE_ARGS },
  754.   { "chapter", cm_chapter, NO_BRACE_ARGS },
  755.   { "cindex", cm_cindex, NO_BRACE_ARGS },
  756.   { "cite", cm_cite, BRACE_ARGS },
  757.   { "clear", cm_clear, NO_BRACE_ARGS },
  758.   { "code", cm_code, BRACE_ARGS },
  759.   { "comment", cm_ignore_line, NO_BRACE_ARGS },
  760.   { "contents", do_nothing, NO_BRACE_ARGS },
  761.   { "copyright", cm_copyright, BRACE_ARGS },
  762.   { "ctrl", cm_ctrl, BRACE_ARGS },
  763.   { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
  764.   { "defindex", cm_defindex, NO_BRACE_ARGS },
  765.   { "dfn", cm_dfn, BRACE_ARGS },
  766.  
  767. /* The `def' commands. */
  768.   { "deffn", cm_defun, NO_BRACE_ARGS },
  769.   { "deffnx", cm_defun, NO_BRACE_ARGS },
  770.   { "defun", cm_defun, NO_BRACE_ARGS },
  771.   { "defunx", cm_defun, NO_BRACE_ARGS },
  772.   { "defmac", cm_defun, NO_BRACE_ARGS },
  773.   { "defmacx", cm_defun, NO_BRACE_ARGS },
  774.   { "defspec", cm_defun, NO_BRACE_ARGS },
  775.   { "defspecx", cm_defun, NO_BRACE_ARGS },
  776.   { "defvr", cm_defun, NO_BRACE_ARGS },
  777.   { "defvrx", cm_defun, NO_BRACE_ARGS },
  778.   { "defvar", cm_defun, NO_BRACE_ARGS },
  779.   { "defvarx", cm_defun, NO_BRACE_ARGS },
  780.   { "defopt", cm_defun, NO_BRACE_ARGS },
  781.   { "defoptx", cm_defun, NO_BRACE_ARGS },
  782.   { "deftypefn", cm_defun, NO_BRACE_ARGS },
  783.   { "deftypefnx", cm_defun, NO_BRACE_ARGS },
  784.   { "deftypefun", cm_defun, NO_BRACE_ARGS },
  785.   { "deftypefunx", cm_defun, NO_BRACE_ARGS },
  786.   { "deftypevr", cm_defun, NO_BRACE_ARGS },
  787.   { "deftypevrx", cm_defun, NO_BRACE_ARGS },
  788.   { "deftypevar", cm_defun, NO_BRACE_ARGS },
  789.   { "deftypevarx", cm_defun, NO_BRACE_ARGS },
  790.   { "defcv", cm_defun, NO_BRACE_ARGS },
  791.   { "defcvx", cm_defun, NO_BRACE_ARGS },
  792.   { "defivar", cm_defun, NO_BRACE_ARGS },
  793.   { "defivarx", cm_defun, NO_BRACE_ARGS },
  794.   { "defop", cm_defun, NO_BRACE_ARGS },
  795.   { "defopx", cm_defun, NO_BRACE_ARGS },
  796.   { "defmethod", cm_defun, NO_BRACE_ARGS },
  797.   { "defmethodx", cm_defun, NO_BRACE_ARGS },
  798.   { "deftypemethod", cm_defun, NO_BRACE_ARGS },
  799.   { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
  800.   { "deftp", cm_defun, NO_BRACE_ARGS },
  801.   { "deftpx", cm_defun, NO_BRACE_ARGS },
  802. /* The end of the `def' commands. */
  803.  
  804.   { "display", cm_display, NO_BRACE_ARGS },
  805.   { "dots", cm_dots, BRACE_ARGS },
  806.   { "dmn", do_nothing, BRACE_ARGS },
  807.   { "emph", cm_emph, BRACE_ARGS },
  808.   { "end", cm_end, NO_BRACE_ARGS },
  809.   { "enumerate", cm_enumerate, NO_BRACE_ARGS },
  810.   { "equiv", cm_equiv, BRACE_ARGS },
  811.   { "error", cm_error, BRACE_ARGS },
  812.   { "example", cm_example, NO_BRACE_ARGS },
  813.   { "exdent", cm_exdent, NO_BRACE_ARGS },
  814.   { "expansion", cm_expansion, BRACE_ARGS },
  815.   { "file", cm_file, BRACE_ARGS },
  816.   { "findex", cm_findex, NO_BRACE_ARGS },
  817.   { "finalout", do_nothing, NO_BRACE_ARGS },
  818.   { "flushleft", cm_flushleft, NO_BRACE_ARGS },
  819.   { "flushright", cm_flushright, NO_BRACE_ARGS },
  820.   { "format", cm_format, NO_BRACE_ARGS },
  821.   { "ftable", cm_ftable, NO_BRACE_ARGS },
  822.   { "group", cm_group, NO_BRACE_ARGS },
  823.   { "heading", cm_heading, NO_BRACE_ARGS },
  824.   { "headings", cm_ignore_line, NO_BRACE_ARGS },
  825.   { "i", cm_italic, BRACE_ARGS },
  826.   { "iappendix", cm_appendix, NO_BRACE_ARGS },
  827.   { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
  828.   { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
  829.   { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  830.   { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  831.   { "ichapter", cm_chapter, NO_BRACE_ARGS },
  832.   { "ifclear", cm_ifclear, NO_BRACE_ARGS },
  833.   { "ifeq", cm_ifeq, NO_BRACE_ARGS },
  834.   { "ifhtml", command_name_condition, NO_BRACE_ARGS },
  835.   { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
  836.   { "ifset", cm_ifset, NO_BRACE_ARGS },
  837.   { "iftex", command_name_condition, NO_BRACE_ARGS },
  838.   { "ignore", command_name_condition, NO_BRACE_ARGS },
  839.   { "include", cm_include, NO_BRACE_ARGS },
  840.   { "inforef", cm_inforef, BRACE_ARGS },
  841.   { "input", cm_include, NO_BRACE_ARGS },
  842.   { "isection", cm_section, NO_BRACE_ARGS },
  843.   { "isubsection", cm_subsection, NO_BRACE_ARGS },
  844.   { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
  845.   { "item", cm_item, NO_BRACE_ARGS },
  846.   { "itemize", cm_itemize, NO_BRACE_ARGS },
  847.   { "itemx", cm_itemx, NO_BRACE_ARGS },
  848.   { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
  849.   { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  850.   { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  851.   { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  852.   { "kbd", cm_kbd, BRACE_ARGS },
  853.   { "key", cm_key, BRACE_ARGS },
  854.   { "kindex", cm_kindex, NO_BRACE_ARGS },
  855.   { "lowersections", cm_lowersections, NO_BRACE_ARGS },
  856.   { "lisp", cm_lisp, NO_BRACE_ARGS },
  857. #if defined (HAVE_MACROS)
  858.   { "macro", cm_macro, NO_BRACE_ARGS },
  859. #endif
  860.   { "majorheading", cm_majorheading, NO_BRACE_ARGS },
  861.   { "math", cm_math, BRACE_ARGS },
  862.   { "medbreak", cm_br, NO_BRACE_ARGS },
  863.   { "menu", cm_menu, NO_BRACE_ARGS },
  864.   { "minus", cm_minus, BRACE_ARGS },
  865.   { "need", cm_ignore_line, NO_BRACE_ARGS },
  866.   { "node", cm_node, NO_BRACE_ARGS },
  867.   { "noindent", cm_noindent, NO_BRACE_ARGS },
  868.   { "nwnode", cm_node, NO_BRACE_ARGS },
  869.   { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
  870.   { "page", do_nothing, NO_BRACE_ARGS },
  871.   { "pindex", cm_pindex, NO_BRACE_ARGS },
  872.   { "point", cm_point, BRACE_ARGS },
  873.   { "print", cm_print, BRACE_ARGS },
  874.   { "printindex", cm_printindex, NO_BRACE_ARGS },
  875.   { "pxref", cm_pxref, BRACE_ARGS },
  876.   { "quotation", cm_quotation, NO_BRACE_ARGS },
  877.   { "r", cm_roman, BRACE_ARGS },
  878.   { "raisesections", cm_raisesections, NO_BRACE_ARGS },
  879.   { "ref", cm_xref, BRACE_ARGS },
  880.   { "refill", cm_refill, NO_BRACE_ARGS },
  881.   { "result", cm_result, BRACE_ARGS },
  882.   { "samp", cm_samp, BRACE_ARGS },
  883.   { "sc", cm_sc, BRACE_ARGS },
  884.   { "section", cm_section, NO_BRACE_ARGS },
  885.   { "set", cm_set, NO_BRACE_ARGS },
  886.   { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
  887.   { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
  888.   { "setfilename", cm_setfilename, NO_BRACE_ARGS },
  889.   { "settitle", cm_ignore_line, NO_BRACE_ARGS },
  890.   { "shortcontents", do_nothing, NO_BRACE_ARGS },
  891.   { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
  892.   { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
  893.   { "smallbreak", cm_br, NO_BRACE_ARGS },
  894.   { "smallexample", cm_smallexample, NO_BRACE_ARGS },
  895.   { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
  896.   { "sp", cm_sp, NO_BRACE_ARGS },
  897.   { "strong", cm_strong, BRACE_ARGS },
  898.   { "subheading", cm_subheading, NO_BRACE_ARGS },
  899.   { "subsection", cm_subsection, NO_BRACE_ARGS },
  900.   { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
  901.   { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
  902.   { "summarycontents", do_nothing, NO_BRACE_ARGS },
  903.   { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
  904.   { "synindex", cm_synindex, NO_BRACE_ARGS },
  905.   { "t", cm_title, BRACE_ARGS },
  906.   { "table", cm_table, NO_BRACE_ARGS },
  907.   { "tex", command_name_condition, NO_BRACE_ARGS },
  908.   { "tindex", cm_tindex, NO_BRACE_ARGS },
  909.   { "titlefont", cm_titlefont, BRACE_ARGS },
  910.   { "titlepage", command_name_condition, NO_BRACE_ARGS },
  911.   { "titlespec", command_name_condition, NO_BRACE_ARGS },
  912.   { "today", cm_today, BRACE_ARGS },
  913.   { "top", cm_top, NO_BRACE_ARGS  },
  914. #if defined (HAVE_MACROS)
  915.   { "unmacro", cm_unmacro, NO_BRACE_ARGS },
  916. #endif
  917.   { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
  918.   { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  919.   { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  920.   { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  921.   { "value", cm_value, BRACE_ARGS },
  922.   { "var", cm_var, BRACE_ARGS },
  923.   { "vindex", cm_vindex, NO_BRACE_ARGS },
  924.   { "vtable", cm_vtable, NO_BRACE_ARGS },
  925.   { "w", cm_w, BRACE_ARGS },
  926.   { "xref", cm_xref, BRACE_ARGS },
  927.   { "{", insert_self, NO_BRACE_ARGS },
  928.   { "}", insert_self, NO_BRACE_ARGS },
  929.  
  930.   /* Some obsoleted commands. */
  931.   { "infotop", cm_obsolete, NO_BRACE_ARGS },
  932.   { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
  933.   { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
  934.   { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
  935.   { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  936.   { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
  937.   { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
  938.   { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
  939.   { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  940.   { "infochapter", cm_obsolete, NO_BRACE_ARGS },
  941.   { "infosection", cm_obsolete, NO_BRACE_ARGS },
  942.   { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
  943.   { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
  944.  
  945.   /* Now @include does what this was supposed to. */
  946.   { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
  947.   { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
  948.   { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
  949.   { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
  950.  
  951.   {(char *) NULL, (COMMAND_FUNCTION *) NULL}, NO_BRACE_ARGS};
  952.  
  953. int major_version = 1;
  954. int minor_version = 63;
  955.  
  956. struct option long_options[] =
  957. {
  958.   { "error-limit", 1, 0, 'e' },            /* formerly -el */
  959.   { "fill-column", 1, 0, 'f' },            /* formerly -fc */
  960.   { "footnote-style", 1, 0, 's' },        /* formerly -ft */
  961.   { "no-headers", 0, &no_headers, 1 },        /* Do not output Node: foo */
  962.   { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
  963.   { "no-validate", 0, &validating, 0 },        /* formerly -nv */
  964.   { "no-split", 0, &splitting, 0 },        /* formerly -ns */
  965.   { "no-warn", 0, &print_warnings, 0 },        /* formerly -nw */
  966. #if defined (HAVE_MACROS)
  967.   { "macro-expand", 1, 0, 'E' },
  968. #endif /* HAVE_MACROS */
  969.   { "number-footnotes", 0, &number_footnotes, 1 },
  970.   { "no-number-footnotes", 0, &number_footnotes, 0 },
  971.   { "output", 1, 0, 'o' },
  972.   { "paragraph-indent", 1, 0, 'p' },        /* formerly -pi */
  973.   { "reference-limit", 1, 0, 'r' },        /* formerly -rl */
  974.   { "verbose", 0, &verbose_mode, 1 },        /* formerly -verbose */
  975.   { "help", 0, 0, 'h' },
  976.   { "version", 0, 0, 'V' },
  977.   {NULL, 0, NULL, 0}
  978. };
  979.  
  980. /* Values for calling handle_variable_internal (). */
  981. #define SET    1
  982. #define CLEAR    2
  983. #define IFSET    3
  984. #define IFCLEAR    4
  985.  
  986. /* **************************************************************** */
  987. /*                                    */
  988. /*            Main ()  Start of code              */
  989. /*                                        */
  990. /* **************************************************************** */
  991.  
  992. /* For each file mentioned in the command line, process it, turning
  993.    texinfo commands into wonderfully formatted output text. */
  994. int
  995. main (int argc, char **argv)
  996. {
  997.   extern int errors_printed;
  998.   int c, ind;
  999.   int reading_from_stdin = 0;
  1000.  
  1001.   /* The name of this program is the last filename in argv[0]. */
  1002.   progname = filename_part (argv[0]);
  1003.  
  1004.   /* Parse argument flags from the input line. */
  1005.   while ((c = getopt_long
  1006.       (argc, argv,
  1007. #if defined (HAVE_MACROS)
  1008.        "D:E:U:I:f:o:p:e:r:s:V",
  1009. #else
  1010.        "D:U:I:f:o:p:e:r:s:V",
  1011. #endif /* !HAVE_MACROS */
  1012.        long_options, &ind))
  1013.      != EOF)
  1014.     {
  1015.       if (c == 0 && long_options[ind].flag == 0)
  1016.     c = long_options[ind].val;
  1017.  
  1018.       switch (c)
  1019.     {
  1020.       /* User specified variable to set or clear? */
  1021.     case 'D':
  1022.     case 'U':
  1023.       handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
  1024.       break;
  1025.  
  1026. #if defined (HAVE_MACROS)
  1027.       /* Use specified a macro expansion output file? */
  1028.     case 'E':
  1029.       if (!macro_expansion_output_stream)
  1030.         {
  1031.           macro_expansion_output_stream = fopen (optarg, "w");
  1032.           if (!macro_expansion_output_stream)
  1033.         error ("Couldn't open macro expansion output \"%s\"", optarg);
  1034.         }
  1035.       else
  1036.         error ("Cannot specify more than one macro expansion output");
  1037.       break;
  1038. #endif /* HAVE_MACROS */
  1039.  
  1040.       /* User specified include file path? */
  1041.     case 'I':
  1042.       if (!include_files_path)
  1043.         include_files_path = strdup (".");
  1044.  
  1045.       include_files_path = (char *)
  1046.         xrealloc (include_files_path,
  1047.               2 + strlen (include_files_path) + strlen (optarg));
  1048.       strcat (include_files_path, ":");
  1049.       strcat (include_files_path, optarg);
  1050.       break;
  1051.  
  1052.       /* User specified fill_column? */
  1053.     case 'f':
  1054.       if (sscanf (optarg, "%d", &fill_column) != 1)
  1055.         usage (stderr, FATAL);
  1056.       break;
  1057.  
  1058.       /* User specified output file? */
  1059.     case 'o':
  1060.       command_output_filename = strdup (optarg);
  1061.       break;
  1062.  
  1063.       /* User specified paragraph indent (paragraph_start_index)? */
  1064.     case 'p':
  1065.       if (set_paragraph_indent (optarg) < 0)
  1066.         usage (stderr, FATAL);
  1067.       break;
  1068.  
  1069.       /* User specified error level? */
  1070.     case 'e':
  1071.       if (sscanf (optarg, "%d", &max_error_level) != 1)
  1072.         usage (stderr, FATAL);
  1073.       break;
  1074.  
  1075.       /* User specified reference warning limit? */
  1076.     case 'r':
  1077.       if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  1078.         usage (stderr, FATAL);
  1079.       break;
  1080.  
  1081.       /* User specified footnote style? */
  1082.     case 's':
  1083.       if (set_footnote_style (optarg) < 0)
  1084.         usage (stderr, FATAL);
  1085.       footnote_style_preset = 1;
  1086.       break;
  1087.  
  1088.     case 'h':
  1089.       usage (stdout, NO_ERROR);
  1090.       break;
  1091.  
  1092.       /* User requested version info? */
  1093.     case 'V':
  1094.       print_version_info ();
  1095.       exit (NO_ERROR);
  1096.       break;
  1097.  
  1098.     case '?':
  1099.       usage (stderr, FATAL);
  1100.       break;
  1101.     }
  1102.     }
  1103.  
  1104.   if (optind == argc)
  1105.     {
  1106.       /* Check to see if input is a file.  If so, process that. */
  1107.       if (!isatty (fileno (stdin)))
  1108.     reading_from_stdin = 1;
  1109.       else
  1110.     usage (stderr, FATAL);
  1111.     }
  1112.  
  1113.   /* If the user has specified --no-headers, this should imply --no-split.
  1114.      Do that here.  I think it might also imply that we should ignore the
  1115.      setfilename at the top of the file, but this might break some FSF things,
  1116.      so I will hold off on that. */
  1117.   if (no_headers)
  1118.     {
  1119.       splitting = 0;
  1120.  
  1121.       /* If the user has not specified an output file, then use stdout by
  1122.      default. */
  1123.       if (!command_output_filename)
  1124.     command_output_filename = strdup ("-");
  1125.     }
  1126.  
  1127.   if (verbose_mode)
  1128.     print_version_info ();
  1129.  
  1130.   /* Remaining arguments are file names of texinfo files.
  1131.      Convert them, one by one. */
  1132.   if (!reading_from_stdin)
  1133.     {
  1134.       while (optind != argc)
  1135.     convert_from_file (argv[optind++]);
  1136.     }
  1137.   else
  1138.     convert_from_stream (stdin, "stdin");
  1139.  
  1140.   if (errors_printed)
  1141.     return (SYNTAX);
  1142.   else
  1143.     return (NO_ERROR);
  1144. }
  1145.  
  1146. /* Display the version info of this invocation of Makeinfo. */
  1147. static void
  1148. print_version_info (void)
  1149. {
  1150.   printf ("This is GNU Makeinfo version %d.%d.\n",
  1151.       major_version, minor_version);
  1152. }
  1153.  
  1154. /* **************************************************************** */
  1155. /*                                    */
  1156. /*            Generic Utilities                */
  1157. /*                                    */
  1158. /* **************************************************************** */
  1159.  
  1160. #if !defined (HAVE_STRDUP)
  1161. char *
  1162. strdup (string)
  1163.      char *string;
  1164. {
  1165.   char *result;
  1166.  
  1167.   result = (char *)xmalloc (1 + strlen (string));
  1168.   strcpy (result, string);
  1169.  
  1170.   return (result);
  1171. }
  1172. #endif /* !HAVE_STRDUP */
  1173.  
  1174. static void
  1175. memory_error (callers_name, bytes_wanted)
  1176.      char *callers_name;
  1177.      int bytes_wanted;
  1178. {
  1179.   char printable_string[80];
  1180.  
  1181.   sprintf (printable_string,
  1182.        "Virtual memory exhausted in %s ()!  Needed %d bytes.",
  1183.        callers_name, bytes_wanted);
  1184.  
  1185.   error (printable_string);
  1186.   abort ();
  1187. }
  1188.  
  1189. /* Just like malloc, but kills the program in case of fatal error. */
  1190. static void *
  1191. xmalloc (unsigned int nbytes)
  1192. {
  1193.   void *temp = (void *) malloc (nbytes);
  1194.  
  1195.   if (nbytes && temp == (void *)NULL)
  1196.     memory_error ("xmalloc", nbytes);
  1197.  
  1198.   return (temp);
  1199. }
  1200.  
  1201. /* Like realloc (), but barfs if there isn't enough memory. */
  1202. static void *
  1203. xrealloc (void *pointer, unsigned int nbytes)
  1204. {
  1205.   void *temp;
  1206.  
  1207.   if (!pointer)
  1208.     temp = (void *)xmalloc (nbytes);
  1209.   else
  1210.     temp = (void *)realloc (pointer, nbytes);
  1211.  
  1212.   if (nbytes && !temp)
  1213.     memory_error ("xrealloc", nbytes);
  1214.  
  1215.   return (temp);
  1216. }
  1217.  
  1218. /* Tell the user how to use this program.
  1219.    Print the message to STREAM, and then exit with EXIT_VALUE. */
  1220. static void
  1221. usage (FILE *stream, int exit_value)
  1222. {
  1223.   fprintf (stream, "Usage: %s [options] texinfo-file...\n\
  1224. \n\
  1225. This program accepts as input files of texinfo commands and text\n\
  1226. and outputs a file suitable for reading with GNU Info.\n\
  1227. \n\
  1228. Options:\n\
  1229. `-I DIR'              add DIR to the directory search list for including\n\
  1230.                       files with the `@include' command.\n\
  1231. -D VAR                define a variable, as with `@set'.\n\
  1232. -U VAR                undefine a variable, as with `@clear'.\n\
  1233. -E MACRO-OFILE        process macros, and output texinfo source code for TeX.\n\
  1234. --no-validate         suppress node cross reference validation.\n\
  1235. --no-warn             suppress warning messages (errors are still output).\n\
  1236. --no-split            suppress the splitting of large files.\n\
  1237. --no-headers          suppress the output of Node: Foo headers.\n\
  1238. --verbose             print information about what is being done.\n\
  1239. --version             print the version number of Makeinfo.\n\
  1240. --output FILE or -o FILE\n\
  1241.                       specify the output file.  When you specify the\n\
  1242.                       output file in this way, any `@setfilename' in the\n\
  1243.                       input file is ignored.\n\
  1244. --paragraph-indent NUM\n\
  1245.                       set the paragraph indent to NUM (default %d).\n\
  1246. --fill-column NUM     set the filling column to NUM (default %d).\n\
  1247. --error-limit NUM     set the error limit to NUM (default %d).\n\
  1248. --reference-limit NUM\n\
  1249.                       set the reference warning limit to NUM (default %d).\n\
  1250. --footnote-style STYLE\n\
  1251.                       set the footnote style to STYLE.  STYLE should\n\
  1252.                       either be `separate' to place footnotes in their own\n\
  1253.                       node, or `end', to place the footnotes at the end of\n\
  1254.                       the node in which they are defined (the default).\n\
  1255. --help                print this message and exit.\n\n",
  1256.        progname, paragraph_start_indent,
  1257.        fill_column, max_error_level, reference_warning_limit);
  1258.   exit (exit_value);
  1259. }
  1260.  
  1261. #if defined (MSDOS)
  1262.  
  1263. static struct passwd *
  1264. getpwnam (char *name)
  1265. {
  1266.   return (struct passwd *) 0;
  1267. }
  1268.  
  1269. #endif /* MSDOS */
  1270.  
  1271. /* **************************************************************** */
  1272. /*                                    */
  1273. /*            Manipulating Lists                  */
  1274. /*                                        */
  1275. /* **************************************************************** */
  1276.  
  1277. typedef struct generic_list {
  1278.   struct generic_list *next;
  1279. } GENERIC_LIST;
  1280.  
  1281. /* Reverse the chain of structures in LIST.  Output the new head
  1282.    of the chain.  You should always assign the output value of this
  1283.    function to something, or you will lose the chain. */
  1284. static GENERIC_LIST *
  1285. reverse_list (register GENERIC_LIST *list)
  1286. {
  1287.   register GENERIC_LIST *next;
  1288.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  1289.  
  1290.   while (list)
  1291.     {
  1292.       next = list->next;
  1293.       list->next = prev;
  1294.       prev = list;
  1295.       list = next;
  1296.     }
  1297.   return (prev);
  1298. }
  1299.  
  1300.  
  1301. /* **************************************************************** */
  1302. /*                                    */
  1303. /*            Pushing and Popping Files               */
  1304. /*                                    */
  1305. /* **************************************************************** */
  1306.  
  1307. /* Find and load the file named FILENAME.  Return a pointer to
  1308.    the loaded file, or NULL if it can't be loaded. */
  1309. static char *
  1310. find_and_load (char *filename)
  1311. {
  1312.   struct stat fileinfo;
  1313.   long file_size;
  1314.   int file = -1, count = 0;
  1315.   char *fullpath, *result;
  1316.  
  1317.   result = fullpath = (char *)NULL;
  1318.  
  1319.   fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
  1320.  
  1321.   if (!fullpath)
  1322.     goto error_exit;
  1323.  
  1324.   filename = fullpath;
  1325.   file_size = (long) fileinfo.st_size;
  1326.  
  1327.   file = open (filename, O_RDONLY);
  1328.   if (file < 0)
  1329.     goto error_exit;
  1330.  
  1331.   /* Load the file. */
  1332.   result = (char *)xmalloc (1 + file_size);
  1333.  
  1334.   /* VMS stat lies about the st_size value.  The actual number of
  1335.      readable bytes is always less than this value.  The arcane
  1336.      mysteries of VMS/RMS are too much to probe, so this hack
  1337.      suffices to make things work.
  1338.     
  1339.      BPW: same business applies under MS-DOS.  */
  1340. #if defined (VMS) || defined (MSDOS)
  1341.   while ((n = read (file, result + count, file_size)) > 0)
  1342.     count += n;
  1343.   if (n == -1)
  1344. #else /* !(VMS && MSDOS) */
  1345.     count = file_size;
  1346.     if (read (file, result, file_size) != file_size)
  1347. #endif /* !(VMS && MSDOS) */
  1348.   error_exit:
  1349.     {
  1350.       if (result)
  1351.     free (result);
  1352.  
  1353.       if (fullpath)
  1354.     free (fullpath);
  1355.  
  1356.       if (file != -1)
  1357.     close (file);
  1358.  
  1359.       return ((char *) NULL);
  1360.     }
  1361.   close (file);
  1362.  
  1363.   /* Set the globals to the new file. */
  1364.   input_text = result;
  1365.   size_of_input_text = count;
  1366.   input_filename = strdup (fullpath);
  1367.   node_filename = strdup (fullpath);
  1368.   input_text_offset = 0;
  1369.   line_number = 1;
  1370.   /* Not strictly necessary.  This magic prevents read_token () from doing
  1371.      extra unnecessary work each time it is called (that is a lot of times).
  1372.      The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
  1373.   input_text[size_of_input_text] = '\n';
  1374.   return (result);
  1375. }
  1376.  
  1377. /* Save the state of the current input file. */
  1378. static void
  1379. pushfile (void)
  1380. {
  1381.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  1382.   newstack->filename = input_filename;
  1383.   newstack->text = input_text;
  1384.   newstack->size = size_of_input_text;
  1385.   newstack->offset = input_text_offset;
  1386.   newstack->line_number = line_number;
  1387.   newstack->next = filestack;
  1388.  
  1389.   filestack = newstack;
  1390.   push_node_filename ();
  1391. }
  1392.  
  1393. /* Make the current file globals be what is on top of the file stack. */
  1394. static void
  1395. popfile (void)
  1396. {
  1397.   FSTACK *tos = filestack;
  1398.  
  1399.   if (!tos)
  1400.     abort ();            /* My fault.  I wonder what I did? */
  1401.  
  1402. #if defined (HAVE_MACROS)
  1403.   if (macro_expansion_output_stream)
  1404.     {
  1405.       maybe_write_itext (input_text, input_text_offset);
  1406.       forget_itext (input_text);
  1407.     }
  1408. #endif /* HAVE_MACROS */
  1409.  
  1410.   /* Pop the stack. */
  1411.   filestack = filestack->next;
  1412.  
  1413.   /* Make sure that commands with braces have been satisfied. */
  1414.   if (!executing_string)
  1415.     discard_braces ();
  1416.  
  1417.   /* Get the top of the stack into the globals. */
  1418.   input_filename = tos->filename;
  1419.   input_text = tos->text;
  1420.   size_of_input_text = tos->size;
  1421.   input_text_offset = tos->offset;
  1422.   line_number = tos->line_number;
  1423.   free (tos);
  1424.  
  1425.   /* Go back to the (now) current node. */
  1426.   pop_node_filename ();
  1427. }
  1428.  
  1429. /* Flush all open files on the file stack. */
  1430. static void
  1431. flush_file_stack (void)
  1432. {
  1433.   while (filestack)
  1434.     {
  1435.       char *fname = input_filename;
  1436.       char *text = input_text;
  1437.       popfile ();
  1438.       free (fname);
  1439.       free (text);
  1440.     }
  1441. }
  1442.  
  1443. int node_filename_stack_index = 0;
  1444. int node_filename_stack_size = 0;
  1445. char **node_filename_stack = (char **)NULL;
  1446.  
  1447. static void
  1448. push_node_filename (void)
  1449. {
  1450.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  1451.     {
  1452.       if (!node_filename_stack)
  1453.     node_filename_stack =
  1454.       (char **)xmalloc ((node_filename_stack_size += 10)
  1455.                 * sizeof (char *));
  1456.       else
  1457.     node_filename_stack =
  1458.       (char **)xrealloc (node_filename_stack,
  1459.                  (node_filename_stack_size + 10)
  1460.                  * sizeof (char *));
  1461.     }
  1462.  
  1463.   node_filename_stack[node_filename_stack_index] = node_filename;
  1464.   node_filename_stack_index++;
  1465. }
  1466.  
  1467. static void
  1468. pop_node_filename (void)
  1469. {
  1470.   node_filename = node_filename_stack[--node_filename_stack_index];
  1471. }
  1472.  
  1473. /* Return just the simple part of the filename; i.e. the
  1474.    filename without the path information, or extensions.
  1475.    This conses up a new string. */
  1476. static char *
  1477. filename_part (char *filename)
  1478. {
  1479.   char *basename;
  1480.  
  1481.   basename = strrchr (filename, '/');
  1482.   if (!basename)
  1483.     basename = filename;
  1484.   else
  1485.     basename++;
  1486.  
  1487.   basename = strdup (basename);
  1488. #if defined (REMOVE_OUTPUT_EXTENSIONS)
  1489.  
  1490.   /* See if there is an extension to remove.  If so, remove it. */
  1491.   {
  1492.     char *temp;
  1493.  
  1494.     temp = strrchr (basename, '.');
  1495.     if (temp)
  1496.       *temp = '\0';
  1497.   }
  1498. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  1499.   return (basename);
  1500. }
  1501.  
  1502. /* Return the pathname part of filename.  This can be NULL. */
  1503. static char *
  1504. pathname_part (char *filename)
  1505. {
  1506.   char *result = (char *) NULL;
  1507.   register int i;
  1508.  
  1509.   filename = expand_filename (filename, "");
  1510.  
  1511.   i = strlen (filename) - 1;
  1512.  
  1513.   while (i && filename[i] != '/')
  1514.     i--;
  1515.   if (filename[i] == '/')
  1516.     i++;
  1517.  
  1518.   if (i)
  1519.     {
  1520.       result = (char *)xmalloc (1 + i);
  1521.       strncpy (result, filename, i);
  1522.       result[i] = '\0';
  1523.     }
  1524.   free (filename);
  1525.   return (result);
  1526. }
  1527.  
  1528. static char *
  1529. filename_non_directory (char *name)
  1530. {
  1531.   register int i;
  1532.  
  1533.   for (i = strlen (name) - 1; i; i--)
  1534.     if (name[i] == '/')
  1535.       return (strdup (name + i + 1));
  1536.  
  1537.   return (strdup (name));
  1538. }
  1539.  
  1540. /* Return the expansion of FILENAME. */
  1541. static char *
  1542. expand_filename (char *filename, char *input_name)
  1543. {
  1544.   register int i;
  1545.  
  1546.   if (filename)
  1547.     filename = full_pathname (filename);
  1548.   else
  1549.     {
  1550.       filename = filename_non_directory (input_name);
  1551.  
  1552.       if (!*filename)
  1553.     {
  1554.       free (filename);
  1555.       filename = strdup ("noname.texi");
  1556.     }
  1557.  
  1558.       for (i = strlen (filename) - 1; i; i--)
  1559.     if (filename[i] == '.')
  1560.       break;
  1561.  
  1562.       if (!i)
  1563.     i = strlen (filename);
  1564.  
  1565.       if (i + 6 > (int) (strlen (filename)))
  1566.     filename = (char *)xrealloc (filename, i + 6);
  1567.       strcpy (filename + i, ".info");
  1568.       return (filename);
  1569.     }
  1570.     
  1571.   if (filename[0] == '.' || filename[0] == '/')
  1572.     return (filename);
  1573.  
  1574.   if (filename[0] != '/' && input_name[0] == '/')
  1575.     {
  1576.       /* Make it so that relative names work. */
  1577.       char *result;
  1578.       
  1579.       i = strlen (input_name) - 1;
  1580.  
  1581.       result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1582.       strcpy (result, input_name);
  1583.  
  1584.       while (result[i] != '/' && i)
  1585.     i--;
  1586.  
  1587.       if (result[i] == '/')
  1588.     i++;
  1589.  
  1590.       strcpy (&result[i], filename);
  1591.       free (filename);
  1592.       return (result);
  1593.     }
  1594.   return (filename);
  1595. }
  1596.  
  1597. /* Return the full path to FILENAME. */
  1598. static char *
  1599. full_pathname (char *filename)
  1600. {
  1601.   int initial_character;
  1602.   char *result;
  1603.  
  1604.   /* No filename given? */
  1605.   if (!filename || !(initial_character = *filename))
  1606.     return (strdup (""));
  1607.   
  1608.   /* Already absolute? */
  1609.   if ((initial_character == '/') ||
  1610.       ((strncmp (filename, "./", 2) == 0) ||
  1611.        (strncmp (filename, "../", 3) == 0)))
  1612.     return (strdup (filename));
  1613.  
  1614.   if (initial_character != '~')
  1615.     {
  1616.       char *localdir;
  1617.  
  1618.       localdir = (char *)xmalloc (1025);
  1619. #if defined (HAVE_GETWD)
  1620.       if (!getwd (localdir))
  1621. #else  /*  !HAVE_GETWD */
  1622.       if (!getcwd (localdir, 1024))
  1623. #endif /* !HAVE_GETWD */
  1624.       {
  1625.         fprintf (stderr, "%s: getwd: %s, %s\n",
  1626.              progname, filename, localdir);
  1627.         exit (1);
  1628.       }
  1629.  
  1630.       strcat (localdir, "/");
  1631.       strcat (localdir, filename);
  1632.       result = strdup (localdir);
  1633.       free (localdir);
  1634.     }
  1635.   else
  1636.     {
  1637.       if (filename[1] == '/')
  1638.     {
  1639.       /* Return the concatenation of the environment variable HOME
  1640.          and the rest of the string. */
  1641.       char *temp_home;
  1642.  
  1643.       temp_home = (char *) getenv ("HOME");
  1644.       result = (char *)xmalloc (strlen (&filename[1])
  1645.                     + 1
  1646.                     + temp_home ? strlen (temp_home)
  1647.                     : 0);
  1648.       *result = '\0';
  1649.  
  1650.       if (temp_home)
  1651.         strcpy (result, temp_home);
  1652.  
  1653.       strcat (result, &filename[1]);
  1654.     }
  1655.       else
  1656.     {
  1657.       struct passwd *user_entry;
  1658.       int i, c;
  1659.       char *username = (char *)xmalloc (257);
  1660.  
  1661.       for (i = 1; (c = filename[i]); i++)
  1662.         {
  1663.           if (c == '/')
  1664.         break;
  1665.           else
  1666.         username[i - 1] = c;
  1667.         }
  1668.       if (c)
  1669.         username[i - 1] = '\0';
  1670.  
  1671.       user_entry = getpwnam (username);
  1672.  
  1673.       if (!user_entry)
  1674.         return (strdup (filename));
  1675.  
  1676.       result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
  1677.                     + strlen (&filename[i]));
  1678.       strcpy (result, user_entry->pw_dir);
  1679.       strcat (result, &filename[i]);
  1680.     }
  1681.     }
  1682.   return (result);
  1683. }
  1684.  
  1685. static char *
  1686. output_name_from_input_name (char *name)
  1687. {
  1688.   return (expand_filename ((char *)NULL, name));
  1689. }
  1690.  
  1691. /* **************************************************************** */
  1692. /*                                    */
  1693. /*            Error Handling                    */
  1694. /*                                    */
  1695. /* **************************************************************** */
  1696.  
  1697. /* Number of errors encountered. */
  1698. int errors_printed = 0;
  1699.  
  1700. /* Print the last error gotten from the file system. */
  1701. static int
  1702. fs_error (char *filename)
  1703. {
  1704.   remember_error ();
  1705.   perror (filename);
  1706.   return (0);
  1707. }
  1708.  
  1709. static int
  1710. error (char *format, ...)
  1711. {
  1712.   va_list ap;
  1713.  
  1714.   remember_error ();
  1715.   va_start (ap, format);
  1716.   vfprintf (stderr, format, ap);
  1717.   fprintf (stderr, "\n");
  1718.   va_end (ap);
  1719.   return ((int) 0);
  1720. }
  1721.  
  1722. /* Just like error (), but print the line number as well. */
  1723. static int
  1724. line_error (char *format, ...)
  1725. {
  1726.   va_list ap;
  1727.  
  1728.   remember_error ();
  1729.   va_start (ap, format);
  1730.   fprintf (stderr, "%s:%d: ", input_filename, line_number);
  1731.   vfprintf (stderr, format, ap);
  1732.   fprintf (stderr, ".\n");
  1733.   va_end (ap);
  1734.   return ((int) 0);
  1735. }
  1736.  
  1737. static int
  1738. warning (char *format, ...)
  1739. {
  1740.   va_list ap;
  1741.  
  1742.   va_start (ap, format);
  1743.   if (print_warnings)
  1744.     {
  1745.       fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
  1746.       vfprintf (stderr, format, ap);
  1747.       fprintf (stderr, ".\n");
  1748.     }
  1749.   va_end (ap);
  1750.   return ((int) 0);
  1751. }
  1752.  
  1753. /* Remember that an error has been printed.  If this is the first
  1754.    error printed, then tell them which program is printing them.
  1755.    If more than max_error_level have been printed, then exit the
  1756.    program. */
  1757. static void
  1758. remember_error (void)
  1759. {
  1760.   errors_printed++;
  1761.   if (max_error_level && (errors_printed > max_error_level))
  1762.     {
  1763.       fprintf (stderr, "Too many errors!  Gave up.\n");
  1764.       flush_file_stack ();
  1765.       cm_bye ();
  1766.       exit (1);
  1767.     }
  1768. }
  1769.  
  1770. /* **************************************************************** */
  1771. /*                                    */
  1772. /*            Hacking Tokens and Strings            */
  1773. /*                                    */
  1774. /* **************************************************************** */
  1775.  
  1776. /* Return the next token as a string pointer.  We cons the
  1777.    string. */
  1778. static char *
  1779. read_token (void)
  1780. {
  1781.   int i, character;
  1782.   char *result;
  1783.  
  1784.   /* If the first character to be read is self-delimiting, then that
  1785.      is the command itself. */
  1786.   character = curchar ();
  1787.   if (self_delimiting (character))
  1788.     {
  1789.       input_text_offset++;
  1790.       result = strdup (" ");
  1791.       *result = character;
  1792.       return (result);
  1793.     }
  1794.  
  1795.   for (i = 0; ((input_text_offset != size_of_input_text)
  1796.            && (character = curchar ())
  1797.            && command_char (character));
  1798.        i++, input_text_offset++);
  1799.   result = (char *)xmalloc (i + 1);
  1800.   memcpy (result, &input_text[input_text_offset - i], i);
  1801.   result[i] = '\0';
  1802.   return (result);
  1803. }
  1804.  
  1805. /* Return non-zero if CHARACTER is self-delimiting. */
  1806. static int
  1807. self_delimiting (int character)
  1808. {
  1809.   return (member (character, "{}:.@*'`,!?; \n\t"));
  1810. }
  1811.  
  1812. /* Clear whitespace from the front and end of string. */
  1813. static void
  1814. canon_white (char *string)
  1815. {
  1816.   int len = strlen (string);
  1817.   int x;
  1818.  
  1819.   if (!len)
  1820.     return;
  1821.  
  1822.   for (x = 0; x < len; x++)
  1823.     {
  1824.       if (!cr_or_whitespace (string[x]))
  1825.     {
  1826.       strcpy (string, string + x);
  1827.       break;
  1828.     }
  1829.     }
  1830.   len = strlen (string);
  1831.   if (len)
  1832.     len--;
  1833.   while (len > -1 && cr_or_whitespace (string[len]))
  1834.     len--;
  1835.   string[len + 1] = '\0';
  1836. }
  1837.  
  1838. /* Bash STRING, replacing all whitespace with just one space. */
  1839. static void
  1840. fix_whitespace (char *string)
  1841. {
  1842.   char *temp = (char *)xmalloc (strlen (string) + 1);
  1843.   int string_index = 0;
  1844.   int temp_index = 0;
  1845.   int c;
  1846.  
  1847.   canon_white (string);
  1848.  
  1849.   while (string[string_index])
  1850.     {
  1851.       c = temp[temp_index++] = string[string_index++];
  1852.  
  1853.       if (c == ' ' || c == '\n' || c == '\t')
  1854.     {
  1855.       temp[temp_index - 1] = ' ';
  1856.       while ((c = string[string_index]) && (c == ' ' ||
  1857.                         c == '\t' ||
  1858.                         c == '\n'))
  1859.         string_index++;
  1860.     }
  1861.     }
  1862.   temp[temp_index] = '\0';
  1863.   strcpy (string, temp);
  1864.   free (temp);
  1865. }
  1866.  
  1867. /* Discard text until the desired string is found.  The string is
  1868.    included in the discarded text. */
  1869. static void
  1870. discard_until (char *string)
  1871. {
  1872.   int temp = search_forward (string, input_text_offset);
  1873.  
  1874.   int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
  1875.   int from = input_text_offset;
  1876.  
  1877.   /* Find out what line we are on. */
  1878.   while (from != tt)
  1879.     if (input_text[from++] == '\n')
  1880.       line_number++;
  1881.  
  1882.   if (temp < 0)
  1883.     {
  1884.       input_text_offset = size_of_input_text - strlen (string);
  1885.  
  1886.       if (strcmp (string, "\n") != 0)
  1887.     {
  1888.       line_error ("Expected `%s'", string);
  1889.       return;
  1890.     }
  1891.     }
  1892.   else
  1893.     input_text_offset = temp;
  1894.  
  1895.   input_text_offset += strlen (string);
  1896. }
  1897.  
  1898. /* Read characters from the file until we are at MATCH.
  1899.    Place the characters read into STRING.
  1900.    On exit input_text_offset is after the match string.
  1901.    Return the offset where the string starts. */
  1902. static int
  1903. get_until (char *match, char **string)
  1904. {
  1905.   int len, current_point, x, new_point, tem;
  1906.  
  1907.   current_point = x = input_text_offset;
  1908.   new_point = search_forward (match, input_text_offset);
  1909.  
  1910.   if (new_point < 0)
  1911.     new_point = size_of_input_text;
  1912.   len = new_point - current_point;
  1913.  
  1914.   /* Keep track of which line number we are at. */
  1915.   tem = new_point + (strlen (match) - 1);
  1916.   while (x != tem)
  1917.     if (input_text[x++] == '\n')
  1918.       line_number++;
  1919.  
  1920.   *string = (char *)xmalloc (len + 1);
  1921.  
  1922.   memcpy (*string, &input_text[current_point], len);
  1923.   (*string)[len] = '\0';
  1924.  
  1925.   /* Now leave input_text_offset in a consistent state. */
  1926.   input_text_offset = tem;
  1927.  
  1928.   if (input_text_offset > size_of_input_text)
  1929.     input_text_offset = size_of_input_text;
  1930.  
  1931.   return (new_point);
  1932. }
  1933.  
  1934. /* Read characters from the file until we are at MATCH or end of line.
  1935.    Place the characters read into STRING.  */
  1936. static void
  1937. get_until_in_line (char *match, char **string)
  1938. {
  1939.   int real_bottom, temp;
  1940.  
  1941.   real_bottom = size_of_input_text;
  1942.   temp = search_forward ("\n", input_text_offset);
  1943.  
  1944.   if (temp < 0)
  1945.     temp = size_of_input_text;
  1946.  
  1947.   size_of_input_text = temp;
  1948.   get_until (match, string);
  1949.   size_of_input_text = real_bottom;
  1950. }
  1951.  
  1952. static void
  1953. get_rest_of_line (char **string)
  1954. {
  1955.   get_until ("\n", string);
  1956.   canon_white (*string);
  1957.  
  1958.   if (curchar () == '\n')    /* as opposed to the end of the file... */
  1959.     {
  1960.       line_number++;
  1961.       input_text_offset++;
  1962.     }
  1963. }
  1964.  
  1965. /* Backup the input pointer to the previous character, keeping track
  1966.    of the current line number. */
  1967. static void
  1968. backup_input_pointer (void)
  1969. {
  1970.   if (input_text_offset)
  1971.     {
  1972.       input_text_offset--;
  1973.       if (curchar () == '\n')
  1974.     line_number--;
  1975.     }
  1976. }
  1977.  
  1978. /* Read characters from the file until we are at MATCH or closing brace.
  1979.    Place the characters read into STRING.  */
  1980. static void
  1981. get_until_in_braces (char *match, char **string)
  1982. {
  1983.   int i, brace = 0;
  1984.   int match_len = strlen (match);
  1985.   char *temp;
  1986.  
  1987.   for (i = input_text_offset; i < size_of_input_text; i++)
  1988.     {
  1989.       if (input_text[i] == '{')
  1990.     brace++;
  1991.       else if (input_text[i] == '}')
  1992.     brace--;
  1993.       else if (input_text[i] == '\n')
  1994.     line_number++;
  1995.  
  1996.       if (brace < 0 ||
  1997.       (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  1998.     break;
  1999.     }
  2000.  
  2001.   match_len = i - input_text_offset;
  2002.   temp = (char *)xmalloc (2 + match_len);
  2003.   strncpy (temp, input_text + input_text_offset, match_len);
  2004.   temp[match_len] = '\0';
  2005.   input_text_offset = i;
  2006.   *string = temp;
  2007. }
  2008.  
  2009. /* **************************************************************** */
  2010. /*                                    */
  2011. /*            Converting the File                 */
  2012. /*                                    */
  2013. /* **************************************************************** */
  2014.  
  2015. /* Convert the file named by NAME.  The output is saved on the file
  2016.    named as the argument to the @setfilename command. */
  2017. static char *suffixes[] = {
  2018.   "",
  2019.   ".texinfo",
  2020.   ".texi",
  2021.   ".txinfo",
  2022.   (char *)NULL
  2023. };
  2024.  
  2025. static void
  2026. initialize_conversion (void)
  2027. {
  2028.   init_tag_table ();
  2029.   init_indices ();
  2030.   init_internals ();
  2031.   init_paragraph ();
  2032. }
  2033.  
  2034.   /* We read in multiples of 4k, simply because it is a typical pipe size
  2035.      on unix systems. */
  2036. #define _READ_BUFFER_GROWTH (4 * 4096)
  2037.  
  2038. /* Convert the texinfo file coming from the open stream STREAM.  Assume the
  2039.    source of the stream is named NAME. */
  2040. static void
  2041. convert_from_stream (FILE *stream, char *name)
  2042. {
  2043.   char *buffer = (char *)NULL;
  2044.   int buffer_offset = 0, buffer_size = 0;
  2045.  
  2046.   initialize_conversion ();
  2047.  
  2048.   /* Read until the end of the stream.  This isn't strictly correct, since
  2049.      the texinfo input may end before the stream ends, but it is a quick
  2050.      working hueristic. */
  2051.   while (!feof (stream))
  2052.     {
  2053.       int count;
  2054.  
  2055.       if (buffer_offset + (_READ_BUFFER_GROWTH + 1) >= buffer_size)
  2056.     buffer = (char *)
  2057.       xrealloc (buffer, (buffer_size += _READ_BUFFER_GROWTH));
  2058.  
  2059.       count = fread (buffer + buffer_offset, 1, _READ_BUFFER_GROWTH, stream);
  2060.  
  2061.       if (count < 0)
  2062.     {
  2063.       perror (name);
  2064.       exit (FATAL);
  2065.     }
  2066.  
  2067.       buffer_offset += count;
  2068.       if (count == 0)
  2069.     break;
  2070.     }
  2071.  
  2072.   /* Set the globals to the new file. */
  2073.   input_text = buffer;
  2074.   size_of_input_text = buffer_offset;
  2075.   input_filename = strdup (name);
  2076.   node_filename = strdup (name);
  2077.   input_text_offset = 0;
  2078.   line_number = 1;
  2079.  
  2080.   /* Not strictly necessary.  This magic prevents read_token () from doing
  2081.      extra unnecessary work each time it is called (that is a lot of times).
  2082.      The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
  2083.   input_text[size_of_input_text] = '\n';
  2084.  
  2085.   convert_from_loaded_file (name);
  2086. }
  2087.  
  2088. static void
  2089. convert_from_file (char *name)
  2090. {
  2091.   register int i;
  2092.   char *filename = (char *)xmalloc (strlen (name) + 50);
  2093.  
  2094.   initialize_conversion ();
  2095.  
  2096.   /* Try to load the file specified by NAME.  If the file isn't found, and
  2097.      there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
  2098.   for (i = 0; suffixes[i]; i++)
  2099.     {
  2100.       strcpy (filename, name);
  2101.       strcat (filename, suffixes[i]);
  2102.  
  2103.       if (find_and_load (filename))
  2104.     break;
  2105.  
  2106.       if (!suffixes[i][0] && strrchr (filename, '.'))
  2107.     {
  2108.       fs_error (filename);
  2109.       free (filename);
  2110.       return;
  2111.     }
  2112.     }
  2113.  
  2114.   if (!suffixes[i])
  2115.     {
  2116.       fs_error (name);
  2117.       free (filename);
  2118.       return;
  2119.     }
  2120.  
  2121.   input_filename = filename;
  2122.  
  2123.   convert_from_loaded_file (name);
  2124. }
  2125.   
  2126. static void
  2127. convert_from_loaded_file (char *name)
  2128. {
  2129.   char *real_output_filename = (char *)NULL;
  2130.  
  2131. #if defined (HAVE_MACROS)
  2132.   remember_itext (input_text, 0);
  2133. #endif /* HAVE_MACROS */
  2134.  
  2135.   /* Search this file looking for the special string which starts conversion.
  2136.      Once found, we may truly begin. */
  2137.   input_text_offset = 0;
  2138.   while (input_text_offset >= 0)
  2139.     {
  2140.       input_text_offset =
  2141.     search_forward (setfilename_search, input_text_offset);
  2142.  
  2143.       if ((input_text_offset == 0) ||
  2144.       ((input_text_offset > 0) &&
  2145.        (input_text[input_text_offset -1] == '\n')))
  2146.     break;
  2147.       else if (input_text_offset > 0)
  2148.     input_text_offset++;
  2149.     }
  2150.  
  2151.   if (input_text_offset < 0)
  2152.     {
  2153.       if (!command_output_filename)
  2154.     {
  2155. #if defined (REQUIRE_SETFILENAME)
  2156.       error ("No `%s' found in `%s'", setfilename_search, name);
  2157.       goto finished;
  2158. #else
  2159.       register int i, end_of_first_line;
  2160.  
  2161.       /* Find the end of the first line in the file. */
  2162.       for (i = 0; i < size_of_input_text - 1; i++)
  2163.         if (input_text[i] == '\n')
  2164.           break;
  2165.  
  2166.       end_of_first_line = i + 1;
  2167.  
  2168.       input_text_offset = 0;
  2169.  
  2170.       for (i = 0; i < end_of_first_line; i++)
  2171.         {
  2172.           if ((input_text[i] == '\\') &&
  2173.           (strncmp (input_text + i + 1, "include", 7) == 0))
  2174.         {
  2175.           input_text_offset = end_of_first_line;
  2176.           break;
  2177.         }
  2178.         }
  2179.       command_output_filename = output_name_from_input_name (name);
  2180. #endif /* !REQUIRE_SETFILENAME */
  2181.     }
  2182.     }
  2183.   else
  2184.     input_text_offset += strlen (setfilename_search);
  2185.  
  2186.   if (!command_output_filename)
  2187.     get_until ("\n", &output_filename);
  2188.   else
  2189.     {
  2190.       if (input_text_offset != -1)
  2191.     discard_until ("\n");
  2192.       else
  2193.     input_text_offset = 0;
  2194.  
  2195.       real_output_filename = output_filename = command_output_filename;
  2196.       command_output_filename = (char *)NULL;
  2197.     }
  2198.  
  2199.   canon_white (output_filename);
  2200.  
  2201.   if (real_output_filename &&
  2202.       strcmp (real_output_filename, "-") == 0)
  2203.     {
  2204.       real_output_filename = strdup (real_output_filename);
  2205.       output_stream = stdout;
  2206.       splitting = 0;        /* Cannot split when writing to stdout. */
  2207.     }
  2208.   else
  2209.     {
  2210.       if (!real_output_filename)
  2211.     real_output_filename = expand_filename (output_filename, name);
  2212.       else
  2213.     real_output_filename = strdup (real_output_filename);
  2214.  
  2215.       output_stream = fopen (real_output_filename, "w");
  2216.     }
  2217.  
  2218.   if (output_stream != stdout)
  2219.     printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  2220.  
  2221.   if (output_stream == NULL)
  2222.     {
  2223.       fs_error (real_output_filename);
  2224.       goto finished;
  2225.     }
  2226.  
  2227.   /* Make the displayable filename from output_filename.  Only the base
  2228.      portion of the filename need be displayed. */
  2229.   if (output_stream != stdout)
  2230.     pretty_output_filename = filename_part (output_filename);
  2231.   else
  2232.     pretty_output_filename = strdup ("stdout");
  2233.  
  2234.   /* For this file only, count the number of newlines from the top of
  2235.      the file to here.  This way, we keep track of line numbers for
  2236.      error reporting.  Line_number starts at 1, since the user isn't
  2237.      zero-based. */
  2238.   {
  2239.     int temp = 0;
  2240.     line_number = 1;
  2241.     while (temp != input_text_offset)
  2242.       if (input_text[temp++] == '\n')
  2243.     line_number++;
  2244.   }
  2245.  
  2246.   if (!no_headers)
  2247.     {
  2248.       add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
  2249.              output_filename, major_version, minor_version);
  2250.       add_word_args ("the input file %s.\n", input_filename);
  2251.     }
  2252.  
  2253.   close_paragraph ();
  2254.   reader_loop ();
  2255.  
  2256. finished:
  2257.   close_paragraph ();
  2258.   flush_file_stack ();
  2259.  
  2260. #if defined (HAVE_MACROS)
  2261.   if (macro_expansion_output_stream)
  2262.     fclose (macro_expansion_output_stream);
  2263. #endif /* HAVE_MACROS */
  2264.  
  2265.   if (output_stream != NULL)
  2266.     {
  2267.       output_pending_notes ();
  2268.       free_pending_notes ();
  2269.       if (tag_table != NULL)
  2270.     {
  2271.       tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
  2272.       if (!no_headers)
  2273.         write_tag_table ();
  2274.     }
  2275.  
  2276.       if (output_stream != stdout)
  2277.     fclose (output_stream);
  2278.  
  2279.       /* If validating, then validate the entire file right now. */
  2280.       if (validating)
  2281.     validate_file (tag_table);
  2282.  
  2283.       /* This used to test  && !errors_printed.
  2284.      But some files might have legit warnings.  So split anyway.  */
  2285.       if (splitting)
  2286.     split_file (real_output_filename, 0);
  2287.     }
  2288.   free (real_output_filename);
  2289. }
  2290.  
  2291. static void
  2292. free_and_clear (char **pointer)
  2293. {
  2294.   if ((*pointer) != (char *) NULL)
  2295.     {
  2296.       free (*pointer);
  2297.       *pointer = (char *) NULL;
  2298.     }
  2299. }
  2300.  
  2301.  /* Initialize some state. */
  2302. static void
  2303. init_internals (void)
  2304. {
  2305.   free_and_clear (¤t_node);
  2306.   free_and_clear (&output_filename);
  2307.   free_and_clear (&command);
  2308.   free_and_clear (&input_filename);
  2309.   free_node_references ();
  2310.   init_insertion_stack ();
  2311.   init_brace_stack ();
  2312.   command_index = 0;
  2313.   in_menu = 0;
  2314.   top_node_seen = 0;
  2315.   non_top_node_seen = 0;
  2316. }
  2317.  
  2318. static void
  2319. init_paragraph (void)
  2320. {
  2321.   free_and_clear ((char **) &output_paragraph);
  2322.   output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
  2323.   output_position = 0;
  2324.   output_paragraph[0] = '\0';
  2325.   output_paragraph_offset = 0;
  2326.   output_column = 0;
  2327.   paragraph_is_open = 0;
  2328.   current_indent = 0;
  2329. }
  2330.  
  2331. /* Okay, we are ready to start the conversion.  Call the reader on
  2332.    some text, and fill the text as it is output.  Handle commands by
  2333.    remembering things like open braces and the current file position on a
  2334.    stack, and when the corresponding close brace is found, you can call
  2335.    the function with the proper arguments. */
  2336. static void
  2337. reader_loop (void)
  2338. {
  2339.   int character;
  2340.   int done = 0;
  2341.   int dash_count = 0;
  2342.  
  2343.   while (!done)
  2344.     {
  2345.       if (input_text_offset >= size_of_input_text)
  2346.     break;
  2347.  
  2348.       character = curchar ();
  2349.  
  2350.       if (!in_fixed_width_font &&
  2351.       (character == '\'' || character == '`') &&
  2352.       input_text[input_text_offset + 1] == character)
  2353.     {
  2354.       input_text_offset++;
  2355.       character = '"';
  2356.     }
  2357.  
  2358.       if (character == '-')
  2359.     {
  2360.       dash_count++;
  2361.       if (dash_count == 2 && !in_fixed_width_font)
  2362.         {
  2363.           input_text_offset++;
  2364.           continue;
  2365.         }
  2366.     }
  2367.       else
  2368.     {
  2369.       dash_count = 0;
  2370.     }
  2371.  
  2372.       /* If this is a whitespace character, then check to see if the line
  2373.      is blank.  If so, advance to the carriage return. */
  2374.       if (whitespace (character))
  2375.     {
  2376.       register int i = input_text_offset + 1;
  2377.  
  2378.       while (i < size_of_input_text && whitespace (input_text[i]))
  2379.         i++;
  2380.  
  2381.       if (i == size_of_input_text || input_text[i] == '\n')
  2382.         {
  2383.           if (i == size_of_input_text)
  2384.         i--;
  2385.  
  2386.           input_text_offset = i;
  2387.           character = curchar ();
  2388.         }
  2389.     }
  2390.  
  2391.       if (character == '\n')
  2392.     {
  2393.       line_number++;
  2394.  
  2395.       /* Check for a menu entry here, since the "escape sequence"
  2396.          that begins menu entrys is "\n* ". */
  2397.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  2398.         {
  2399.           char *tem;
  2400.  
  2401.           /* Note that the value of TEM is discarded, since it is
  2402.          gauranteed to be NULL when glean_node_from_menu () is
  2403.          called with a non-zero argument. */
  2404.           tem = glean_node_from_menu (1);
  2405.         }
  2406.     }
  2407.  
  2408.       switch (character)
  2409.     {
  2410.     case COMMAND_PREFIX:
  2411.       read_command ();
  2412.       break;
  2413.  
  2414.     case '{':
  2415.  
  2416.       /* Special case.  I'm not supposed to see this character by itself.
  2417.          If I do, it means there is a syntax error in the input text.
  2418.          Report the error here, but remember this brace on the stack so
  2419.          you can ignore its partner. */
  2420.  
  2421.       line_error ("Misplaced `{'");
  2422.       remember_brace (misplaced_brace);
  2423.  
  2424.       /* Don't advance input_text_offset since this happens in
  2425.          remember_brace ().
  2426.          input_text_offset++;
  2427.            */
  2428.       break;
  2429.  
  2430.     case '}':
  2431.       pop_and_call_brace ();
  2432.       input_text_offset++;
  2433.       break;
  2434.  
  2435.     default:
  2436.       add_char (character);
  2437.       input_text_offset++;
  2438.     }
  2439.     }
  2440. #if defined (HAVE_MACROS)
  2441.   if (macro_expansion_output_stream)
  2442.     maybe_write_itext (input_text, input_text_offset);
  2443. #endif /* HAVE_MACROS */
  2444. }
  2445.  
  2446. /* Find the command corresponding to STRING.  If the command
  2447.    is found, return a pointer to the data structure.  Otherwise
  2448.    return (-1). */
  2449. static COMMAND *
  2450. get_command_entry (char *string)
  2451. {
  2452.   register int i;
  2453.  
  2454.   for (i = 0; CommandTable[i].name; i++)
  2455.     if (strcmp (CommandTable[i].name, string) == 0)
  2456.       return (&CommandTable[i]);
  2457.  
  2458.   /* This command is not in our predefined command table.  Perhaps
  2459.      it is a user defined command. */
  2460.   for (i = 0; i < user_command_array_len; i++)
  2461.     if (user_command_array[i] &&
  2462.     (strcmp (user_command_array[i]->name, string) == 0))
  2463.       return (user_command_array[i]);
  2464.  
  2465.   /* Nope, we never heard of this command. */
  2466.   return ((COMMAND *) -1);
  2467. }
  2468.  
  2469. /* input_text_offset is right at the command prefix character.
  2470.    Read the next token to determine what to do. */
  2471. static void
  2472. read_command (void)
  2473. {
  2474.   COMMAND *entry;
  2475.  
  2476.   input_text_offset++;
  2477.   free_and_clear (&command);
  2478.   command = read_token ();
  2479.  
  2480. #if defined (HAVE_MACROS)
  2481.   /* Check to see if this command is a macro.  If so, execute it here. */
  2482.   {
  2483.     MACRO_DEF *def;
  2484.  
  2485.     def = find_macro (command);
  2486.  
  2487.     if (def)
  2488.       {
  2489.     /* We disallow recursive use of a macro call.  Inhibit the expansion
  2490.        of this macro during the life of its execution. */
  2491.     if (!(def->flags & ME_RECURSE))
  2492.       def->inhibited = 1;
  2493.  
  2494.     execute_macro (def);
  2495.  
  2496.     if (!(def->flags & ME_RECURSE))
  2497.       def->inhibited = 0;
  2498.  
  2499.     return;
  2500.       }
  2501.     }
  2502. #endif /* HAVE_MACROS */
  2503.  
  2504.   entry = get_command_entry (command);
  2505.  
  2506.   if (entry == (COMMAND *)-1)
  2507.     {
  2508.       line_error ("Unknown info command `%s'", command);
  2509.       return;
  2510.     }
  2511.  
  2512.   if (entry->argument_in_braces)
  2513.     remember_brace (entry->proc);
  2514.  
  2515.   (*(entry->proc)) (START, output_paragraph_offset, 0);
  2516. }
  2517.  
  2518. /* Return the string which invokes PROC; a pointer to a function. */
  2519. static char *
  2520. find_proc_name (COMMAND_FUNCTION *proc)
  2521. {
  2522.   register int i;
  2523.  
  2524.   for (i = 0; CommandTable[i].name; i++)
  2525.     if (proc == CommandTable[i].proc)
  2526.       return (CommandTable[i].name);
  2527.   return ("NO_NAME!");
  2528. }
  2529.  
  2530. static void
  2531. init_brace_stack (void)
  2532. {
  2533.   brace_stack = (BRACE_ELEMENT *) NULL;
  2534. }
  2535.  
  2536. static void
  2537. remember_brace (COMMAND_FUNCTION *proc)
  2538. {
  2539.   if (curchar () != '{')
  2540.     line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command);
  2541.   else
  2542.     input_text_offset++;
  2543.   remember_brace_1 (proc, output_paragraph_offset);
  2544. }
  2545.  
  2546. /* Remember the current output position here.  Save PROC
  2547.    along with it so you can call it later. */
  2548. static void
  2549. remember_brace_1 (COMMAND_FUNCTION *proc, int position)
  2550. {
  2551.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  2552.   new->next = brace_stack;
  2553.   new->proc = proc;
  2554.   new->pos = position;
  2555.   new->line = line_number;
  2556.   brace_stack = new;
  2557. }
  2558.  
  2559. /* Pop the top of the brace stack, and call the associated function
  2560.    with the args END and POS. */
  2561. static void
  2562. pop_and_call_brace (void)
  2563. {
  2564.   BRACE_ELEMENT *temp;
  2565.   COMMAND_FUNCTION *proc;
  2566.   int pos;
  2567.  
  2568.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  2569.     {
  2570.       line_error ("Unmatched close brace");
  2571.       return;
  2572.     }
  2573.  
  2574.   pos = brace_stack->pos;
  2575.   proc = brace_stack->proc;
  2576.   temp = brace_stack->next;
  2577.   free (brace_stack);
  2578.   brace_stack = temp;
  2579.  
  2580.   (*proc) (END, pos, output_paragraph_offset);
  2581. }
  2582.  
  2583. /* Shift all of the markers in `brace_stack' by AMOUNT. */
  2584. static void
  2585. adjust_braces_following (int here, int amount)
  2586. {
  2587.   register BRACE_ELEMENT *stack = brace_stack;
  2588.  
  2589.   while (stack)
  2590.     {
  2591.       if (stack->pos >= here)
  2592.     stack->pos += amount;
  2593.       stack = stack->next;
  2594.     }
  2595. }
  2596.  
  2597. /* You call discard_braces () when you shouldn't have any braces on the stack.
  2598.    I used to think that this happens for commands that don't take arguments
  2599.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  2600.    I only detect it at the beginning of nodes. */
  2601. static void
  2602. discard_braces (void)
  2603. {
  2604.   if (!brace_stack)
  2605.     return;
  2606.  
  2607.   while (brace_stack)
  2608.     {
  2609.       if (brace_stack->proc != misplaced_brace)
  2610.     {
  2611.       char *proc_name;
  2612.       int temp_line_number = line_number;
  2613.  
  2614.       line_number = brace_stack->line;
  2615.       proc_name = find_proc_name (brace_stack->proc);
  2616.       line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name);
  2617.       line_number = temp_line_number;
  2618.       pop_and_call_brace ();
  2619.     }
  2620.       else
  2621.     {
  2622.       BRACE_ELEMENT *temp;
  2623.       temp = brace_stack->next;
  2624.       free (brace_stack);
  2625.       brace_stack = temp;
  2626.     }
  2627.     }
  2628. }
  2629.  
  2630. static int
  2631. get_char_len (int character)
  2632. {
  2633.   /* Return the printed length of the character. */
  2634.   int len;
  2635.  
  2636.   switch (character)
  2637.     {
  2638.     case '\t':
  2639.       len = (output_column + 8) & 0xf7;
  2640.       if (len > fill_column)
  2641.     len = fill_column - output_column;
  2642.       else
  2643.     len = len - output_column;
  2644.       break;
  2645.  
  2646.     case '\n':
  2647.       len = fill_column - output_column;
  2648.       break;
  2649.  
  2650.     default:
  2651.       if (character < ' ')
  2652.     len = 2;
  2653.       else
  2654.     len = 1;
  2655.     }
  2656.   return (len);
  2657. }
  2658.  
  2659. static void
  2660. add_word_args (char *format, ...)
  2661. {
  2662.   va_list ap;
  2663.   char buffer[1000];
  2664.  
  2665.   va_start (ap, format);
  2666.   vsprintf (buffer, format, ap);
  2667.   add_word (buffer);
  2668.   va_end (ap);
  2669. }
  2670.  
  2671. /* Add STRING to output_paragraph. */
  2672. static void
  2673. add_word (char *string)
  2674. {
  2675.   while (*string)
  2676.     add_char (*string++);
  2677. }
  2678.  
  2679. /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
  2680. int last_char_was_newline = 1;
  2681.  
  2682. /* The actual last inserted character.  Note that this may be something
  2683.    other than NEWLINE even if last_char_was_newline is 1. */
  2684. int last_inserted_character = 0;
  2685.  
  2686. /* Non-zero means that a newline character has already been
  2687.    inserted, so close_paragraph () should insert one less. */
  2688. int line_already_broken = 0;
  2689.  
  2690. /* When non-zero we have finished an insertion (see end_insertion ()) and we
  2691.    want to ignore false continued paragraph closings. */
  2692. int insertion_paragraph_closed = 0;
  2693.  
  2694. /* Non-zero means attempt to make all of the lines have fill_column width. */
  2695. int do_justification = 0;
  2696.  
  2697. /* Add the character to the current paragraph.  If filling_enabled is
  2698.    non-zero, then do filling as well. */
  2699. static void
  2700. add_char (int character)
  2701. {
  2702.   /* If we are avoiding outputting headers, and we are currently
  2703.      in a menu, then simply return. */
  2704.   if (no_headers && in_menu)
  2705.     return;
  2706.  
  2707.   /* If we are adding a character now, then we don't have to
  2708.      ignore close_paragraph () calls any more. */
  2709.   if (must_start_paragraph && character != '\n')
  2710.     {
  2711.       must_start_paragraph = 0;
  2712.       line_already_broken = 0;    /* The line is no longer broken. */
  2713.       if (current_indent > output_column)
  2714.     {
  2715.       indent (current_indent - output_column);
  2716.       output_column = current_indent;
  2717.     }
  2718.     }
  2719.  
  2720.   if (non_splitting_words && member (character, " \t\n"))
  2721.     character = ' ' | 0x80;
  2722.  
  2723.   insertion_paragraph_closed = 0;
  2724.  
  2725.   switch (character)
  2726.     {
  2727.     case '\n':
  2728.       if (!filling_enabled)
  2729.     {
  2730.       insert ('\n');
  2731.  
  2732.       if (force_flush_right)
  2733.         {
  2734.           close_paragraph ();
  2735.           /* Hack to force single blank lines out in this mode. */
  2736.           flush_output ();
  2737.         }
  2738.  
  2739.       output_column = 0;
  2740.  
  2741.       if (!no_indent && paragraph_is_open)
  2742.         indent (output_column = current_indent);
  2743.       break;
  2744.     }
  2745.       else /* CHARACTER is newline, and filling is enabled. */
  2746.     {
  2747.       if (sentence_ender (last_inserted_character))
  2748.         {
  2749.           insert (' ');
  2750.           output_column++;
  2751.           last_inserted_character = character;
  2752.         }
  2753.     }
  2754.  
  2755.       if (last_char_was_newline)
  2756.     {
  2757.       close_paragraph ();
  2758.       pending_indent = 0;
  2759.     }
  2760.       else
  2761.     {
  2762.       last_char_was_newline = 1;
  2763.       insert (' ');
  2764.       output_column++;
  2765.     }
  2766.       break;
  2767.  
  2768.     default:
  2769.       {
  2770.     int len = get_char_len (character);
  2771.     int suppress_insert = 0;
  2772.  
  2773.     if ((character == ' ') && (last_char_was_newline))
  2774.       {
  2775.         if (!paragraph_is_open)
  2776.           {
  2777.         pending_indent++;
  2778.         return;
  2779.           }
  2780.       }
  2781.  
  2782.     if (!paragraph_is_open)
  2783.       {
  2784.         start_paragraph ();
  2785.  
  2786.         /* If the paragraph is supposed to be indented a certain way,
  2787.            then discard all of the pending whitespace.  Otherwise, we
  2788.            let the whitespace stay. */
  2789.         if (!paragraph_start_indent)
  2790.           indent (pending_indent);
  2791.         pending_indent = 0;
  2792.       }
  2793.  
  2794.     if ((output_column += len) > fill_column)
  2795.       {
  2796.         if (filling_enabled)
  2797.           {
  2798.         int temp = output_paragraph_offset;
  2799.         while (--temp > 0 && output_paragraph[temp] != '\n')
  2800.           {
  2801.             /* If we have found a space, we have the place to break
  2802.                the line. */
  2803.             if (output_paragraph[temp] == ' ')
  2804.               {
  2805.             /* Remove trailing whitespace from output. */
  2806.             while (temp && whitespace (output_paragraph[temp - 1]))
  2807.               temp--;
  2808.  
  2809.             output_paragraph[temp++] = '\n';
  2810.  
  2811.             /* We have correctly broken the line where we want
  2812.                to.  What we don't want is spaces following where
  2813.                we have decided to break the line.  We get rid of
  2814.                them. */
  2815.             {
  2816.               int t1 = temp;
  2817.  
  2818.               for (;; t1++)
  2819.                 {
  2820.                   if (t1 == output_paragraph_offset)
  2821.                 {
  2822.                   if (whitespace (character))
  2823.                     suppress_insert = 1;
  2824.                   break;
  2825.                 }
  2826.                   if (!whitespace (output_paragraph[t1]))
  2827.                 break;
  2828.                 }
  2829.  
  2830.               if (t1 != temp)
  2831.                 {
  2832.                   adjust_braces_following (temp, (- (t1 - temp)));
  2833.                   strncpy ((char *) &output_paragraph[temp],
  2834.                        (char *) &output_paragraph[t1],
  2835.                        (output_paragraph_offset - t1));
  2836.                   output_paragraph_offset -= (t1 - temp);
  2837.                 }
  2838.             }
  2839.  
  2840.             /* Filled, but now indent if that is right. */
  2841.             if (indented_fill && current_indent)
  2842.               {
  2843.                 int buffer_len = ((output_paragraph_offset - temp)
  2844.                           + current_indent);
  2845.                 char *temp_buffer = (char *)xmalloc (buffer_len);
  2846.                 int indentation = 0;
  2847.  
  2848.                 /* We have to shift any markers that are in
  2849.                    front of the wrap point. */
  2850.                 adjust_braces_following (temp, current_indent);
  2851.  
  2852.                 while (current_indent > 0 &&
  2853.                    indentation != current_indent)
  2854.                   temp_buffer[indentation++] = ' ';
  2855.  
  2856.                 strncpy ((char *) &temp_buffer[current_indent],
  2857.                      (char *) &output_paragraph[temp],
  2858.                      buffer_len - current_indent);
  2859.  
  2860.                 if (output_paragraph_offset + buffer_len
  2861.                 >= paragraph_buffer_len)
  2862.                   {
  2863.                 unsigned char *tt = xrealloc
  2864.                   (output_paragraph,
  2865.                    (paragraph_buffer_len += buffer_len));
  2866.                 output_paragraph = tt;
  2867.                   }
  2868.                 strncpy ((char *) &output_paragraph[temp],
  2869.                      temp_buffer, buffer_len);
  2870.                 output_paragraph_offset += current_indent;
  2871.                 free (temp_buffer);
  2872.               }
  2873.             output_column = 0;
  2874.             while (temp < output_paragraph_offset)
  2875.               output_column +=
  2876.                 get_char_len (output_paragraph[temp++]);
  2877.             output_column += len;
  2878.             break;
  2879.               }
  2880.           }
  2881.           }
  2882.       }
  2883.  
  2884.     if (!suppress_insert)
  2885.       {
  2886.         insert (character);
  2887.         last_inserted_character = character;
  2888.       }
  2889.     last_char_was_newline = 0;
  2890.     line_already_broken = 0;
  2891.       }
  2892.     }
  2893. }
  2894.  
  2895. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  2896. static void
  2897. insert (int character)
  2898. {
  2899.   output_paragraph[output_paragraph_offset++] = character;
  2900.   if (output_paragraph_offset == paragraph_buffer_len)
  2901.     {
  2902.       output_paragraph =
  2903.     xrealloc (output_paragraph, (paragraph_buffer_len += 100));
  2904.     }
  2905. }
  2906.  
  2907. /* Remove upto COUNT characters of whitespace from the
  2908.    the current output line.  If COUNT is less than zero,
  2909.    then remove until none left. */
  2910. static void
  2911. kill_self_indent (int count)
  2912. {
  2913.   /* Handle infinite case first. */
  2914.   if (count < 0)
  2915.     {
  2916.       output_column = 0;
  2917.       while (output_paragraph_offset)
  2918.     {
  2919.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2920.         output_paragraph_offset--;
  2921.       else
  2922.         break;
  2923.     }
  2924.     }
  2925.   else
  2926.     {
  2927.       while (output_paragraph_offset && count--)
  2928.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2929.       output_paragraph_offset--;
  2930.     else
  2931.       break;
  2932.     }
  2933. }
  2934.  
  2935. /* Non-zero means do not honor calls to flush_output (). */
  2936. static int flushing_ignored = 0;
  2937.  
  2938. /* Prevent calls to flush_output () from having any effect. */
  2939. static void
  2940. inhibit_output_flushing (void)
  2941. {
  2942.   flushing_ignored++;
  2943. }
  2944.  
  2945. /* Allow calls to flush_output () to write the paragraph data. */
  2946. static void
  2947. uninhibit_output_flushing (void)
  2948. {
  2949.   flushing_ignored--;
  2950. }
  2951.  
  2952. static void
  2953. flush_output (void)
  2954. {
  2955.   register int i;
  2956.  
  2957.   if (!output_paragraph_offset || flushing_ignored)
  2958.     return;
  2959.  
  2960.   for (i = 0; i < output_paragraph_offset; i++)
  2961.     {
  2962.       if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
  2963.       output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
  2964.       output_paragraph[i] == (unsigned char)('\n' | 0x80) ||
  2965.       sentence_ender (UNMETA (output_paragraph[i])))
  2966.     output_paragraph[i] &= 0x7f;
  2967.     }
  2968.  
  2969.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  2970.  
  2971.   output_position += output_paragraph_offset;
  2972.   output_paragraph_offset = 0;
  2973. }
  2974.  
  2975. /* How to close a paragraph controlling the number of lines between
  2976.    this one and the last one. */
  2977.  
  2978. /* Paragraph spacing is controlled by this variable.  It is the number of
  2979.    blank lines that you wish to appear between paragraphs.  A value of
  2980.    1 creates a single blank line between paragraphs. */
  2981. int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
  2982.  
  2983. /* Close the current paragraph, leaving no blank lines between them. */
  2984. static void
  2985. close_single_paragraph (void)
  2986. {
  2987.   close_paragraph_with_lines (0);
  2988. }
  2989.  
  2990. /* Close a paragraph after an insertion has ended. */
  2991. static void
  2992. close_insertion_paragraph (void)
  2993. {
  2994.   if (!insertion_paragraph_closed)
  2995.     {
  2996.       /* Close the current paragraph, breaking the line. */
  2997.       close_single_paragraph ();
  2998.  
  2999.       /* Start a new paragraph here, inserting whatever indention is correct
  3000.      for the now current insertion level (one above the one that we are
  3001.      ending). */
  3002.       start_paragraph ();
  3003.  
  3004.       /* Tell close_paragraph () that the previous line has already been
  3005.      broken, so it should insert one less newline. */
  3006.       line_already_broken = 1;
  3007.  
  3008.       /* Let functions such as add_char () know that we have already found a
  3009.      newline. */
  3010.       ignore_blank_line ();
  3011.     }
  3012.   else
  3013.     {
  3014.       /* If the insertion paragraph is closed already, then we are seeing
  3015.      two `@end' commands in a row.  Note that the first one we saw was
  3016.      handled in the first part of this if-then-else clause, and at that
  3017.      time start_paragraph () was called, partially to handle the proper
  3018.      indentation of the current line.  However, the indentation level
  3019.      may have just changed again, so we may have to outdent the current
  3020.      line to the new indentation level. */
  3021.       if (current_indent < output_column)
  3022.     kill_self_indent (output_column - current_indent);
  3023.     }
  3024.  
  3025.   insertion_paragraph_closed = 1;
  3026. }
  3027.  
  3028. static void
  3029. close_paragraph_with_lines (int lines)
  3030. {
  3031.   int old_spacing = paragraph_spacing;
  3032.   paragraph_spacing = lines;
  3033.   close_paragraph ();
  3034.   paragraph_spacing = old_spacing;
  3035. }
  3036.  
  3037. /* Close the currently open paragraph. */
  3038. static void
  3039. close_paragraph (void)
  3040. {
  3041.   register int i;
  3042.  
  3043.   /* The insertion paragraph is no longer closed. */
  3044.   insertion_paragraph_closed = 0;
  3045.  
  3046.   if (paragraph_is_open && !must_start_paragraph)
  3047.     {
  3048.       register int tindex, c;
  3049.  
  3050.       tindex = output_paragraph_offset;
  3051.  
  3052.       /* Back up to last non-newline/space character, forcing all such
  3053.      subsequent characters to be newlines.  This isn't strictly
  3054.      necessary, but a couple of functions use the presence of a newline
  3055.      to make decisions. */
  3056.       for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
  3057.     {
  3058.       c = output_paragraph[tindex];
  3059.  
  3060.       if (c == ' '|| c == '\n')
  3061.         output_paragraph[tindex] = '\n';
  3062.       else
  3063.         break;
  3064.     }
  3065.  
  3066.       /* All trailing whitespace is ignored. */
  3067.       output_paragraph_offset = ++tindex;
  3068.  
  3069.       /* Break the line if that is appropriate. */
  3070.       if (paragraph_spacing >= 0)
  3071.     insert ('\n');
  3072.  
  3073.       /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
  3074.       if (!force_flush_right)
  3075.     {
  3076.       for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
  3077.         insert ('\n');
  3078.     }
  3079.  
  3080.       /* If we are doing flush right indentation, then do it now
  3081.      on the paragraph (really a single line). */
  3082.       if (force_flush_right)
  3083.     do_flush_right_indentation ();
  3084.  
  3085.       flush_output ();
  3086.       paragraph_is_open = 0;
  3087.       no_indent = 0;
  3088.       output_column = 0;
  3089.     }
  3090.   ignore_blank_line ();
  3091. }
  3092.  
  3093. /* Make the last line just read look as if it were only a newline. */
  3094. static void
  3095. ignore_blank_line (void)
  3096. {
  3097.   last_inserted_character = '\n';
  3098.   last_char_was_newline = 1;
  3099. }
  3100.  
  3101. /* Align the end of the text in output_paragraph with fill_column. */
  3102. static void
  3103. do_flush_right_indentation (void)
  3104. {
  3105.   char *temp;
  3106.   int temp_len;
  3107.  
  3108.   kill_self_indent (-1);
  3109.  
  3110.   if (output_paragraph[0] != '\n')
  3111.     {
  3112.       output_paragraph[output_paragraph_offset] = '\0';
  3113.  
  3114.       if (output_paragraph_offset < fill_column)
  3115.     {
  3116.       register int i;
  3117.  
  3118.       if (fill_column >= paragraph_buffer_len)
  3119.         output_paragraph =
  3120.           xrealloc (output_paragraph,
  3121.             (paragraph_buffer_len += fill_column));
  3122.  
  3123.       temp_len = strlen ((char *)output_paragraph);
  3124.       temp = (char *)xmalloc (temp_len + 1);
  3125.       memcpy (temp, (char *)output_paragraph, temp_len);
  3126.  
  3127.       for (i = 0; i < fill_column - output_paragraph_offset; i++)
  3128.         output_paragraph[i] = ' ';
  3129.  
  3130.       memcpy ((char *)output_paragraph + i, temp, temp_len);
  3131.       free (temp);
  3132.       output_paragraph_offset = fill_column;
  3133.     }
  3134.     }
  3135. }
  3136.  
  3137. /* Begin a new paragraph. */
  3138. static void
  3139. start_paragraph (void)
  3140. {
  3141.   /* First close existing one. */
  3142.   if (paragraph_is_open)
  3143.     close_paragraph ();
  3144.  
  3145.   /* In either case, the insertion paragraph is no longer closed. */
  3146.   insertion_paragraph_closed = 0;
  3147.  
  3148.   /* However, the paragraph is open! */
  3149.   paragraph_is_open = 1;
  3150.  
  3151.   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
  3152.      had to be called before we would allow any other paragraph operations
  3153.      to have an effect. */
  3154.   if (!must_start_paragraph)
  3155.     {
  3156.       int amount_to_indent = 0;
  3157.  
  3158.       /* If doing indentation, then insert the appropriate amount. */
  3159.       if (!no_indent)
  3160.     {
  3161.       if (inhibit_paragraph_indentation)
  3162.         {
  3163.           amount_to_indent = current_indent;
  3164.           if (inhibit_paragraph_indentation < 0)
  3165.         inhibit_paragraph_indentation++;
  3166.         }
  3167.       else if (paragraph_start_indent < 0)
  3168.         amount_to_indent = current_indent;
  3169.       else
  3170.         amount_to_indent = current_indent + paragraph_start_indent;
  3171.  
  3172.       if (amount_to_indent >= output_column)
  3173.         {
  3174.           amount_to_indent -= output_column;
  3175.           indent (amount_to_indent);
  3176.           output_column += amount_to_indent;
  3177.         }
  3178.     }
  3179.     }
  3180.   else
  3181.     must_start_paragraph = 0;
  3182. }
  3183.  
  3184. /* Insert the indentation specified by AMOUNT. */
  3185. static void
  3186. indent (int amount)
  3187. {
  3188.   register BRACE_ELEMENT *elt = brace_stack;
  3189.  
  3190.   /* For every START_POS saved within the brace stack which will be affected
  3191.      by this indentation, bump that start pos forward. */
  3192.   while (elt)
  3193.     {
  3194.       if (elt->pos >= output_paragraph_offset)
  3195.     elt->pos += amount;
  3196.       elt = elt->next;
  3197.     }
  3198.  
  3199.   while (--amount >= 0)
  3200.     insert (' ');
  3201. }
  3202.  
  3203. /* Search forward for STRING in input_text.
  3204.    FROM says where where to start. */
  3205. static int
  3206. search_forward (char *string, int from)
  3207. {
  3208.   int len = strlen (string);
  3209.  
  3210.   while (from < size_of_input_text)
  3211.     {
  3212.       if (strncmp (input_text + from, string, len) == 0)
  3213.     return (from);
  3214.       from++;
  3215.     }
  3216.   return (-1);
  3217. }
  3218.  
  3219. /* Whoops, Unix doesn't have strcasecmp. */
  3220.  
  3221. /* Case independent string compare. */
  3222. #if !defined (HAVE_STRCASECMP)
  3223. static int
  3224. strcasecmp (char *string1, char *string2)
  3225. {
  3226.   char ch1, ch2;
  3227.  
  3228.   for (;;)
  3229.     {
  3230.       ch1 = *string1++;
  3231.       ch2 = *string2++;
  3232.  
  3233.       if (!(ch1 | ch2))
  3234.     return (0);
  3235.  
  3236.       ch1 = coerce_to_upper (ch1);
  3237.       ch2 = coerce_to_upper (ch2);
  3238.  
  3239.       if (ch1 != ch2)
  3240.     return (ch1 - ch2);
  3241.     }
  3242. }
  3243. #endif /* !HAVE_STRCASECMP */
  3244.  
  3245. enum insertion_type { menu, quotation, lisp, smalllisp, example,
  3246.   smallexample, display, itemize, format, enumerate, cartouche, table,
  3247.   ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
  3248.   defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
  3249.   deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
  3250.   deftypemethod, deftp, bad_type };
  3251.  
  3252. char *insertion_type_names[] = { "menu", "quotation", "lisp",
  3253.   "smalllisp", "example", "smallexample", "display", "itemize",
  3254.   "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
  3255.   "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
  3256.   "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
  3257.   "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
  3258.   "defivar", "defop", "defmethod", "deftypemethod", "deftp",
  3259.   "bad_type" };
  3260.  
  3261. int insertion_level = 0;
  3262. typedef struct istack_elt
  3263. {
  3264.   struct istack_elt *next;
  3265.   char *item_function;
  3266.   char *filename;
  3267.   int line_number;
  3268.   int filling_enabled;
  3269.   int indented_fill;
  3270.   enum insertion_type insertion;
  3271.   int inhibited;
  3272. } INSERTION_ELT;
  3273.  
  3274. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  3275.  
  3276. static void
  3277. init_insertion_stack (void)
  3278. {
  3279.   insertion_stack = (INSERTION_ELT *) NULL;
  3280. }
  3281.  
  3282. /* Return the type of the current insertion. */
  3283. static enum insertion_type
  3284. current_insertion_type (void)
  3285. {
  3286.   if (!insertion_level)
  3287.     return (bad_type);
  3288.   else
  3289.     return (insertion_stack->insertion);
  3290. }
  3291.  
  3292. /* Return a pointer to the string which is the function to wrap around
  3293.    items. */
  3294. static char *
  3295. current_item_function (void)
  3296. {
  3297.   register int level, done;
  3298.   register INSERTION_ELT *elt;
  3299.  
  3300.   level = insertion_level;
  3301.   elt = insertion_stack;
  3302.   done = 0;
  3303.  
  3304.   /* Skip down through the stack until we find a non-conditional insertion. */
  3305.   while (!done && (elt != NULL))
  3306.     {
  3307.       switch (elt->insertion)
  3308.     {
  3309.     case ifinfo:
  3310.     case ifset:
  3311.     case ifclear:
  3312.     case cartouche:
  3313.       elt = elt->next;
  3314.       level--;
  3315.       break;
  3316.  
  3317.     default:
  3318.       done = 1;
  3319.     }
  3320.     }
  3321.  
  3322.   if (!level)
  3323.     return ((char *) NULL);
  3324.   else
  3325.     return (elt->item_function);
  3326. }
  3327.  
  3328. static char *
  3329. get_item_function (void)
  3330. {
  3331.   char *item_function;
  3332.   get_rest_of_line (&item_function);
  3333.   backup_input_pointer ();
  3334.   canon_white (item_function);
  3335.   return (item_function);
  3336. }
  3337.  
  3338.  /* Push the state of the current insertion on the stack. */
  3339. static void
  3340. push_insertion (enum insertion_type type, char *item_function)
  3341. {
  3342.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  3343.  
  3344.   new->item_function = item_function;
  3345.   new->filling_enabled = filling_enabled;
  3346.   new->indented_fill = indented_fill;
  3347.   new->insertion = type;
  3348.   new->line_number = line_number;
  3349.   new->filename = strdup (input_filename);
  3350.   new->inhibited = inhibit_paragraph_indentation;
  3351.   new->next = insertion_stack;
  3352.   insertion_stack = new;
  3353.   insertion_level++;
  3354. }
  3355.  
  3356.  /* Pop the value on top of the insertion stack into the
  3357.     global variables. */
  3358. static void
  3359. pop_insertion (void)
  3360. {
  3361.   INSERTION_ELT *temp = insertion_stack;
  3362.  
  3363.   if (temp == (INSERTION_ELT *) NULL)
  3364.     return;
  3365.  
  3366.   inhibit_paragraph_indentation = temp->inhibited;
  3367.   filling_enabled = temp->filling_enabled;
  3368.   indented_fill = temp->indented_fill;
  3369.   free_and_clear (&(temp->item_function));
  3370.   free_and_clear (&(temp->filename));
  3371.   insertion_stack = insertion_stack->next;
  3372.   free (temp);
  3373.   insertion_level--;
  3374. }
  3375.  
  3376.  /* Return a pointer to the print name of this
  3377.     enumerated type. */
  3378. static char *
  3379. insertion_type_pname (enum insertion_type type)
  3380. {
  3381.   if ((int) type < (int) bad_type)
  3382.     return (insertion_type_names[(int) type]);
  3383.   else
  3384.     return ("Broken-Type in insertion_type_pname");
  3385. }
  3386.  
  3387. /* Return the insertion_type associated with NAME.
  3388.    If the type is not one of the known ones, return BAD_TYPE. */
  3389. static enum insertion_type
  3390. find_type_from_name (char *name)
  3391. {
  3392.   int index = 0;
  3393.   while (index < (int) bad_type)
  3394.     {
  3395.       if (strcmp (name, insertion_type_names[index]) == 0)
  3396.     return (enum insertion_type) index;
  3397.       index++;
  3398.     }
  3399.   return (bad_type);
  3400. }
  3401.  
  3402. static void
  3403. do_nothing (void)
  3404. {
  3405. }
  3406.  
  3407. static int
  3408. defun_insertion (enum insertion_type type)
  3409. {
  3410.   return
  3411.     ((type == deffn)
  3412.      || (type == defun)
  3413.      || (type == defmac)
  3414.      || (type == defspec)
  3415.      || (type == defvr)
  3416.      || (type == defvar)
  3417.      || (type == defopt)
  3418.      || (type == deftypefn)
  3419.      || (type == deftypefun)
  3420.      || (type == deftypevr)
  3421.      || (type == deftypevar)
  3422.      || (type == defcv)
  3423.      || (type == defivar)
  3424.      || (type == defop)
  3425.      || (type == defmethod)
  3426.      || (type == deftypemethod)
  3427.      || (type == deftp));
  3428. }
  3429.  
  3430. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  3431.    which seemed reasonable.  This doesn't control the number of items,
  3432.    just the number of nested lists. */
  3433. #define max_stack_depth 100
  3434. #define ENUM_DIGITS 1
  3435. #define ENUM_ALPHA  2
  3436. typedef struct {
  3437.   int enumtype;
  3438.   int enumval;
  3439. } DIGIT_ALPHA;
  3440.  
  3441. DIGIT_ALPHA enumstack[max_stack_depth];
  3442. int enumstack_offset = 0;
  3443. int current_enumval = 1;
  3444. int current_enumtype = ENUM_DIGITS;
  3445. char *enumeration_arg = (char *)NULL;
  3446.  
  3447. static void
  3448. start_enumerating (int at, int type)
  3449. {
  3450.   if ((enumstack_offset + 1) == max_stack_depth)
  3451.     {
  3452.       line_error ("Enumeration stack overflow");
  3453.       return;
  3454.     }
  3455.   enumstack[enumstack_offset].enumtype = current_enumtype;
  3456.   enumstack[enumstack_offset].enumval = current_enumval;
  3457.   enumstack_offset++;
  3458.   current_enumval = at;
  3459.   current_enumtype = type;
  3460. }
  3461.  
  3462. static void
  3463. stop_enumerating (void)
  3464. {
  3465.   --enumstack_offset;
  3466.   if (enumstack_offset < 0)
  3467.     enumstack_offset = 0;
  3468.  
  3469.   current_enumval = enumstack[enumstack_offset].enumval;
  3470.   current_enumtype = enumstack[enumstack_offset].enumtype;
  3471. }
  3472.  
  3473. /* Place a letter or digits into the output stream. */
  3474. static void
  3475. enumerate_item (void)
  3476. {
  3477.   char temp[10];
  3478.  
  3479.   if (current_enumtype == ENUM_ALPHA)
  3480.     {
  3481.       if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
  3482.     {
  3483.       current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
  3484.       warning ("Lettering overflow, restarting at %c", current_enumval);
  3485.     }
  3486.       sprintf (temp, "%c. ", current_enumval);
  3487.     }
  3488.   else
  3489.     sprintf (temp, "%d. ", current_enumval);
  3490.  
  3491.   indent (output_column += (current_indent - strlen (temp)));
  3492.   add_word (temp);
  3493.   current_enumval++;
  3494. }
  3495.  
  3496. /* This is where the work for all the "insertion" style
  3497.    commands is done.  A huge switch statement handles the
  3498.    various setups, and generic code is on both sides. */
  3499. static void
  3500. begin_insertion (enum insertion_type type)
  3501. {
  3502.   int no_discard = 0;
  3503.  
  3504.   if (defun_insertion (type))
  3505.     {
  3506.       push_insertion (type, strdup (""));
  3507.       no_discard++;
  3508.     }
  3509.   else
  3510.     push_insertion (type, get_item_function ());
  3511.  
  3512.   switch (type)
  3513.     {
  3514.     case menu:
  3515.       if (!no_headers)
  3516.     close_paragraph ();
  3517.  
  3518.       filling_enabled = no_indent = 0;
  3519.       inhibit_paragraph_indentation = 1;
  3520.  
  3521.       if (!no_headers)
  3522.     add_word ("* Menu:\n");
  3523.  
  3524.       in_menu++;
  3525.       no_discard++;
  3526.       break;
  3527.  
  3528.       /* I think @quotation is meant to do filling.
  3529.      If you don't want filling, then use @example. */
  3530.     case quotation:
  3531.       close_single_paragraph ();
  3532.       last_char_was_newline = no_indent = 0;
  3533.       indented_fill = filling_enabled = 1;
  3534.       inhibit_paragraph_indentation = 1;
  3535.       current_indent += default_indentation_increment;
  3536.       break;
  3537.  
  3538.     case display:
  3539.     case example:
  3540.     case smallexample:
  3541.     case lisp:
  3542.     case smalllisp:
  3543.       /* Just like @example, but no indentation. */
  3544.     case format:
  3545.  
  3546.       close_single_paragraph ();
  3547.       inhibit_paragraph_indentation = 1;
  3548.       in_fixed_width_font++;
  3549.       filling_enabled = 0;
  3550.       last_char_was_newline = 0;
  3551.  
  3552.       if (type != format)
  3553.     current_indent += default_indentation_increment;
  3554.  
  3555.       break;
  3556.  
  3557.     case table:
  3558.     case ftable:
  3559.     case vtable:
  3560.     case itemize:
  3561.       close_single_paragraph ();
  3562.       current_indent += default_indentation_increment;
  3563.       filling_enabled = indented_fill = 1;
  3564. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3565.       inhibit_paragraph_indentation = 0;
  3566. #else
  3567.       inhibit_paragraph_indentation = 1;
  3568. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3569.  
  3570.       /* Make things work for losers who forget the itemize syntax. */
  3571.       if (allow_lax_format && (type == itemize))
  3572.     {
  3573.       if (!(*insertion_stack->item_function))
  3574.         {
  3575.           free (insertion_stack->item_function);
  3576.           insertion_stack->item_function = strdup ("@bullet");
  3577.           insertion_stack->item_function[0] = COMMAND_PREFIX;
  3578.         }
  3579.     }
  3580.  
  3581.       if (!*insertion_stack->item_function)
  3582.     {
  3583.       line_error ("%s requires an argument: the formatter for %citem",
  3584.               insertion_type_pname (type), COMMAND_PREFIX);
  3585.     }
  3586.       break;
  3587.  
  3588.     case enumerate:
  3589.       close_single_paragraph ();
  3590.       no_indent = 0;
  3591. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3592.       inhibit_paragraph_indentation = 0;
  3593. #else
  3594.       inhibit_paragraph_indentation = 1;
  3595. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3596.  
  3597.       current_indent += default_indentation_increment;
  3598.       filling_enabled = indented_fill = 1;
  3599.  
  3600.       if (isdigit (*enumeration_arg))
  3601.     start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
  3602.       else
  3603.     start_enumerating (*enumeration_arg, ENUM_ALPHA);
  3604.       break;
  3605.  
  3606.       /* Does nothing special in makeinfo. */
  3607.     case group:
  3608.       /* Only close the paragraph if we are not inside of an @example. */
  3609.       if (!insertion_stack->next ||
  3610.       insertion_stack->next->insertion != example)
  3611.     close_single_paragraph ();
  3612.       break;
  3613.  
  3614.       /* Insertions that are no-ops in info, but do something in TeX. */
  3615.     case ifinfo:
  3616.     case ifset:
  3617.     case ifclear:
  3618.     case cartouche:
  3619.       if (in_menu)
  3620.     no_discard++;
  3621.       break;
  3622.  
  3623.     case deffn:
  3624.     case defun:
  3625.     case defmac:
  3626.     case defspec:
  3627.     case defvr:
  3628.     case defvar:
  3629.     case defopt:
  3630.     case deftypefn:
  3631.     case deftypefun:
  3632.     case deftypevr:
  3633.     case deftypevar:
  3634.     case defcv:
  3635.     case defivar:
  3636.     case defop:
  3637.     case defmethod:
  3638.     case deftypemethod:
  3639.     case deftp:
  3640.       inhibit_paragraph_indentation = 1;
  3641.       filling_enabled = indented_fill = 1;
  3642.       current_indent += default_indentation_increment;
  3643.       no_indent = 0;
  3644.       break;
  3645.  
  3646.     case flushleft:
  3647.       close_single_paragraph ();
  3648.       inhibit_paragraph_indentation = 1;
  3649.       filling_enabled = indented_fill = no_indent = 0;
  3650.       break;
  3651.  
  3652.     case flushright:
  3653.       close_single_paragraph ();
  3654.       filling_enabled = indented_fill = no_indent = 0;
  3655.       inhibit_paragraph_indentation = 1;
  3656.       force_flush_right++;
  3657.       break;
  3658.  
  3659.     default:
  3660.       break;
  3661.     }
  3662.  
  3663.   if (!no_discard)
  3664.     discard_until ("\n");
  3665. }
  3666.  
  3667. /* Try to end the insertion with the specified TYPE.
  3668.    TYPE, with a value of bad_type,  gets translated to match
  3669.    the value currently on top of the stack.
  3670.    Otherwise, if TYPE doesn't match the top of the insertion stack,
  3671.    give error. */
  3672. static void
  3673. end_insertion (enum insertion_type type)
  3674. {
  3675.   enum insertion_type temp_type;
  3676.  
  3677.   if (!insertion_level)
  3678.     return;
  3679.  
  3680.   temp_type = current_insertion_type ();
  3681.  
  3682.   if (type == bad_type)
  3683.     type = temp_type;
  3684.  
  3685.   if (type != temp_type)
  3686.     {
  3687.       line_error
  3688.     ("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX,
  3689.      insertion_type_pname (temp_type), insertion_type_pname (type));
  3690.       return;
  3691.     }
  3692.  
  3693.   pop_insertion ();
  3694.  
  3695.   switch (type)
  3696.     {
  3697.       /* Insertions which have no effect on paragraph formatting. */
  3698.     case ifinfo:
  3699.     case ifset:
  3700.     case ifclear:
  3701.       break;
  3702.  
  3703.     case menu:
  3704.       in_menu--;        /* No longer hacking menus. */
  3705.       if (!no_headers)
  3706.     close_insertion_paragraph ();
  3707.       break;
  3708.  
  3709.     case enumerate:
  3710.       stop_enumerating ();
  3711.       close_insertion_paragraph ();
  3712.       current_indent -= default_indentation_increment;
  3713.       break;
  3714.  
  3715.     case flushleft:
  3716.     case group:
  3717.     case cartouche:
  3718.       close_insertion_paragraph ();
  3719.       break;
  3720.  
  3721.     case format:
  3722.     case display:
  3723.     case example:
  3724.     case smallexample:
  3725.     case lisp:
  3726.     case smalllisp:
  3727.     case quotation:
  3728.  
  3729.       /* @quotation is the only one of the above without a fixed width
  3730.      font. */
  3731.       if (type != quotation)
  3732.     in_fixed_width_font--;
  3733.  
  3734.       /* @format is the only fixed_width insertion without a change
  3735.      in indentation. */
  3736.       if (type != format)
  3737.     current_indent -= default_indentation_increment;
  3738.  
  3739.       /* The ending of one of these insertions always marks the
  3740.      start of a new paragraph. */
  3741.       close_insertion_paragraph ();
  3742.       break;
  3743.  
  3744.     case table:
  3745.     case ftable:
  3746.     case vtable:
  3747.     case itemize:
  3748.       current_indent -= default_indentation_increment;
  3749.       break;
  3750.  
  3751.     case flushright:
  3752.       force_flush_right--;
  3753.       close_insertion_paragraph ();
  3754.       break;
  3755.  
  3756.       /* Handle the @defun style insertions with a default clause. */
  3757.     default:
  3758.       current_indent -= default_indentation_increment;
  3759.       close_insertion_paragraph ();
  3760.       break;
  3761.     }
  3762. }
  3763.  
  3764. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  3765.    code that creates such boundaries, you should call discard_insertions ()
  3766.    before doing anything else.  It prints the errors for you, and cleans up
  3767.    the insertion stack. */
  3768. static void
  3769. discard_insertions (void)
  3770. {
  3771.   int real_line_number = line_number;
  3772.   while (insertion_stack)
  3773.     {
  3774.       if (insertion_stack->insertion == ifinfo ||
  3775.       insertion_stack->insertion == ifset ||
  3776.       insertion_stack->insertion == ifclear ||
  3777.       insertion_stack->insertion == cartouche)
  3778.     break;
  3779.       else
  3780.     {
  3781.       char *offender;
  3782.       char *current_filename;
  3783.  
  3784.       current_filename = input_filename;
  3785.       offender = (char *)insertion_type_pname (insertion_stack->insertion);
  3786.       input_filename = insertion_stack->filename;
  3787.       line_number = insertion_stack->line_number;
  3788.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  3789.               COMMAND_PREFIX, offender);
  3790.       input_filename = current_filename;
  3791.       pop_insertion ();
  3792.     }
  3793.     }
  3794.   line_number = real_line_number;
  3795. }
  3796.  
  3797. /* The actual commands themselves. */
  3798.  
  3799. /* Commands which insert themselves. */
  3800. static void
  3801. insert_self (void)
  3802. {
  3803.   add_word (command);
  3804. }
  3805.  
  3806. /* Force a line break in the output. */
  3807. static void
  3808. cm_asterisk (void)
  3809. {
  3810.   close_single_paragraph ();
  3811. #if !defined (ASTERISK_NEW_PARAGRAPH)
  3812.   cm_noindent ();
  3813. #endif /* ASTERISK_NEW_PARAGRAPH */
  3814. }
  3815.  
  3816. /* Insert ellipsis. */
  3817. static void
  3818. cm_dots (int arg)
  3819. {
  3820.   if (arg == START)
  3821.     add_word ("...");
  3822. }
  3823.  
  3824. static void
  3825. cm_bullet (int arg)
  3826. {
  3827.   if (arg == START)
  3828.     add_char ('*');
  3829. }
  3830.  
  3831. static void
  3832. cm_minus (int arg)
  3833. {
  3834.   if (arg == START)
  3835.     add_char ('-');
  3836. }
  3837.  
  3838. /* Insert "TeX". */
  3839. static void
  3840. cm_TeX (int arg)
  3841. {
  3842.   if (arg == START)
  3843.     add_word ("TeX");
  3844. }
  3845.  
  3846. static void
  3847. cm_copyright (int arg)
  3848. {
  3849.   if (arg == START)
  3850.     add_word ("(C)");
  3851. }
  3852.  
  3853. #if defined (__osf__)
  3854. #define LOCALTIME_CAST(x) (time_t *)(x)
  3855. #else
  3856. #define LOCALTIME_CAST(x) (x)
  3857. #endif
  3858.  
  3859. static void
  3860. cm_today (int arg)
  3861. {
  3862.   static char * months [12] =
  3863.     { "January", "February", "March", "April", "May", "June", "July",
  3864.     "August", "September", "October", "November", "December" };
  3865.   if (arg == START)
  3866.     {
  3867.       long timer = time (0);
  3868.       struct tm *ts = localtime (LOCALTIME_CAST (&timer));
  3869.       add_word_args
  3870.     ("%d %s %d",
  3871.      (ts -> tm_mday),
  3872.      (months [ts -> tm_mon]),
  3873.      ((ts -> tm_year) + 1900));
  3874.     }
  3875. }
  3876.  
  3877. static void
  3878. cm_code (int arg)
  3879. {
  3880.   extern int printing_index;
  3881.  
  3882.   if (printing_index)
  3883.     return;
  3884.  
  3885.   if (arg == START)
  3886.     {
  3887.       in_fixed_width_font++;
  3888.       add_char ('`');
  3889.     }
  3890.   else
  3891.     {
  3892.       add_word ("'");
  3893.       in_fixed_width_font--;
  3894.     }
  3895. }
  3896.  
  3897. static void
  3898. cm_samp (int arg)
  3899. {
  3900.   cm_code (arg);
  3901. }
  3902.  
  3903. static void
  3904. cm_file (int arg)
  3905. {
  3906.   cm_code (arg);
  3907. }
  3908.  
  3909. static void
  3910. cm_kbd (int arg)
  3911. {
  3912.   cm_code (arg);
  3913. }
  3914.  
  3915. static void
  3916. cm_key (int arg)
  3917. {
  3918. }
  3919.  
  3920. /* Convert the character at position into CTL. */
  3921. static void
  3922. cm_ctrl (int arg, int start, int end)
  3923. {
  3924.   /* Should we allow multiple character arguments?  I think yes. */
  3925.   if (arg == END)
  3926.     {
  3927.       register int i, character;
  3928. #if defined (NO_MULTIPLE_CTRL)
  3929.       if ((end - start) != 1)
  3930.     line_error ("%c%s expects a single character as an argument",
  3931.             COMMAND_PREFIX, command);
  3932.       else
  3933. #endif
  3934.     for (i = start; i < end; i++)
  3935.       {
  3936.         character = output_paragraph[i];
  3937.  
  3938.         if (isletter (character))
  3939.           output_paragraph[i] = CTL (coerce_to_upper (character));
  3940.       }
  3941.     }
  3942. }
  3943.  
  3944. /* Small Caps in makeinfo just does all caps. */
  3945. static void
  3946. cm_sc (int arg, int start_pos, int end_pos)
  3947. {
  3948.   if (arg == END)
  3949.     {
  3950.       while (start_pos < end_pos)
  3951.     {
  3952.       output_paragraph[start_pos] =
  3953.         coerce_to_upper (output_paragraph[start_pos]);
  3954.       start_pos++;
  3955.     }
  3956.     }
  3957. }
  3958.  
  3959. /* @var in makeinfo just uppercases the text. */
  3960. static void
  3961. cm_var (int arg, int start_pos, int end_pos)
  3962. {
  3963.   if (arg == END)
  3964.     {
  3965.       while (start_pos < end_pos)
  3966.     {
  3967.       output_paragraph[start_pos] =
  3968.         coerce_to_upper (output_paragraph[start_pos]);
  3969.       start_pos++;
  3970.     }
  3971.     }
  3972. }
  3973.  
  3974. static void
  3975. cm_dfn (int arg, int position)
  3976. {
  3977.   add_char ('"');
  3978. }
  3979.  
  3980. static void
  3981. cm_emph (int arg)
  3982. {
  3983.   add_char ('*');
  3984. }
  3985.  
  3986. static void
  3987. cm_strong (int arg, int position)
  3988. {
  3989.   cm_emph (arg);
  3990. }
  3991.  
  3992. static void
  3993. cm_cite (int arg, int position)
  3994. {
  3995.   if (arg == START)
  3996.     add_word ("`");
  3997.   else
  3998.     add_word ("'");
  3999. }
  4000.  
  4001. /* Current text is italicized. */
  4002. static void
  4003. cm_italic (int arg, int start, int end)
  4004. {
  4005. }
  4006.  
  4007. /* Current text is highlighted. */
  4008. static void
  4009. cm_bold (int arg, int start, int end)
  4010. {
  4011.   cm_italic (arg, start, end);
  4012. }
  4013.  
  4014. /* Current text is in roman font. */
  4015. static void
  4016. cm_roman (int arg, int start, int end)
  4017. {
  4018. }
  4019.  
  4020. /* Current text is in roman font. */
  4021. static void
  4022. cm_titlefont (int arg, int start, int end)
  4023. {
  4024. }
  4025.  
  4026. /* Italicize titles. */
  4027. static void
  4028. cm_title (int arg, int start, int end)
  4029. {
  4030.   cm_italic (arg, start, end);
  4031. }
  4032.  
  4033. /* @refill is a NOP. */
  4034. static void
  4035. cm_refill (void)
  4036. {
  4037. }
  4038.  
  4039. /* Prevent the argument from being split across two lines. */
  4040. static void
  4041. cm_w (int arg, int start, int end)
  4042. {
  4043.   if (arg == START)
  4044.     non_splitting_words++;
  4045.   else
  4046.     non_splitting_words--;
  4047. }
  4048.  
  4049.  
  4050. /* Explain that this command is obsolete, thus the user shouldn't
  4051.    do anything with it. */
  4052. static void
  4053. cm_obsolete (int arg, int start, int end)
  4054. {
  4055.   if (arg == START)
  4056.     warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command);
  4057. }
  4058.  
  4059. /* Insert the text following input_text_offset up to the end of the line
  4060.    in a new, separate paragraph.  Directly underneath it, insert a
  4061.    line of WITH_CHAR, the same length of the inserted text. */
  4062. static void
  4063. insert_and_underscore (int with_char)
  4064. {
  4065.   register int i, len;
  4066.   int old_no_indent, starting_pos, ending_pos;
  4067.   char *temp;
  4068.  
  4069.   close_paragraph ();
  4070.   filling_enabled =  indented_fill = 0;
  4071.   old_no_indent = no_indent;
  4072.   no_indent = 1;
  4073.  
  4074. #if defined (HAVE_MACROS)
  4075.   if (macro_expansion_output_stream)
  4076.     append_to_expansion_output (input_text_offset + 1);
  4077. #endif /* HAVE_MACROS */
  4078.  
  4079.   get_rest_of_line (&temp);
  4080.  
  4081.   starting_pos = output_position + output_paragraph_offset;
  4082. #if defined (HAVE_MACROS)
  4083.   if (macro_expansion_output_stream)
  4084.     {
  4085.       char *temp1;
  4086.  
  4087.       temp1 = (char *)xmalloc (2 + strlen (temp));
  4088.       sprintf (temp1, "%s\n", temp);
  4089.       remember_itext (input_text, input_text_offset);
  4090.       me_execute_string (temp1);
  4091.       free (temp1);
  4092.     }
  4093.   else
  4094. #endif /* HAVE_MACROS */
  4095.   execute_string ("%s\n", temp);
  4096.  
  4097.   ending_pos = output_position + output_paragraph_offset;
  4098.   free (temp);
  4099.  
  4100.   len = (ending_pos - starting_pos) - 1;
  4101.   for (i = 0; i < len; i++)
  4102.     add_char (with_char);
  4103.   insert ('\n');
  4104.   close_paragraph ();
  4105.   filling_enabled = 1;
  4106.   no_indent = old_no_indent;
  4107. }
  4108.  
  4109. /* Here is a structure which associates sectioning commands with
  4110.    an integer, hopefully to reflect the `depth' of the current
  4111.    section. */
  4112. struct {
  4113.   char *name;
  4114.   int level;
  4115. } section_alist[] = {
  4116.   { "unnumberedsubsubsec", 5 },
  4117.   { "unnumberedsubsec", 4 },
  4118.   { "unnumberedsec", 3 },
  4119.   { "unnumbered", 2 },
  4120.   { "appendixsubsubsec", 5 },
  4121.   { "appendixsubsec", 4 },
  4122.   { "appendixsec", 3 },
  4123.   { "appendixsection", 3 },
  4124.   { "appendix", 2 },
  4125.   { "subsubsec", 5 },
  4126.   { "subsubsection", 5 },
  4127.   { "subsection", 4 },
  4128.   { "section", 3 },
  4129.   { "chapter", 2 },
  4130.   { "top", 1 },
  4131.  
  4132.   { (char *)NULL, 0 }
  4133. };
  4134.  
  4135. /* Amount to offset the name of sectioning commands to levels by. */
  4136. int section_alist_offset = 0;
  4137.  
  4138. /* Shift the meaning of @section to @chapter. */
  4139. static void
  4140. cm_raisesections (void)
  4141. {
  4142.   discard_until ("\n");
  4143.   section_alist_offset--;
  4144. }
  4145.  
  4146. /* Shift the meaning of @chapter to @section. */
  4147. static void
  4148. cm_lowersections (void)
  4149. {
  4150.   discard_until ("\n");
  4151.   section_alist_offset++;
  4152. }
  4153.  
  4154. /* Return an integer which identifies the type section present in TEXT. */
  4155. static int
  4156. what_section (char *text)
  4157. {
  4158.   register int i, j;
  4159.   char *t;
  4160.  
  4161.  find_section_command:
  4162.   for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
  4163.   if (text[j] != COMMAND_PREFIX)
  4164.     return (-1);
  4165.  
  4166.   text = text + j + 1;
  4167.  
  4168.   /* We skip @c, @comment, and @?index commands. */
  4169.   if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
  4170.       (text[0] == 'c' && cr_or_whitespace (text[1])) ||
  4171.       (strcmp (text + 1, "index") == 0))
  4172.     {
  4173.       while (*text++ != '\n');
  4174.       goto find_section_command;
  4175.     }
  4176.  
  4177.   /* Handle italicized sectioning commands. */
  4178.   if (*text == 'i')
  4179.     text++;
  4180.  
  4181.   for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
  4182.  
  4183.   for (i = 0; (t = section_alist[i].name); i++)
  4184.     {
  4185.       if (j == strlen (t) && strncmp (t, text, j) == 0)
  4186.     {
  4187.       int return_val;
  4188.  
  4189.       return_val = (section_alist[i].level + section_alist_offset);
  4190.  
  4191.       if (return_val < 0)
  4192.         return_val = 0;
  4193.       else if (return_val > 5)
  4194.         return_val = 5;
  4195.       return (return_val);
  4196.     }
  4197.     }
  4198.   return (-1);
  4199. }
  4200.  
  4201. /* Set the level of @top to LEVEL.  Return the old level of @top. */
  4202. static int
  4203. set_top_section_level (int level)
  4204. {
  4205.   register int i, result = -1;
  4206.  
  4207.   for (i = 0; section_alist[i].name; i++)
  4208.     if (strcmp (section_alist[i].name, "top") == 0)
  4209.       {
  4210.     result = section_alist[i].level;
  4211.     section_alist[i].level = level;
  4212.     break;
  4213.       }
  4214.   return (result);
  4215. }
  4216.  
  4217. /* Treat this just like @unnumbered.  The only difference is
  4218.    in node defaulting. */
  4219. static void
  4220. cm_top (void)
  4221. {
  4222.   /* It is an error to have more than one @top. */
  4223.   if (top_node_seen)
  4224.     {
  4225.       TAG_ENTRY *tag = tag_table;
  4226.  
  4227.       line_error ("There already is a node having %ctop as a section",
  4228.           COMMAND_PREFIX);
  4229.  
  4230.       while (tag != (TAG_ENTRY *)NULL)
  4231.     {
  4232.       if ((tag->flags & IS_TOP))
  4233.         {
  4234.           int old_line_number = line_number;
  4235.           char *old_input_filename = input_filename;
  4236.  
  4237.           line_number = tag->line_no;
  4238.           input_filename = tag->filename;
  4239.           line_error ("Here is the %ctop node", COMMAND_PREFIX);
  4240.           input_filename = old_input_filename;
  4241.           line_number = old_line_number;
  4242.           return;
  4243.         }
  4244.       tag = tag->next_ent;
  4245.     }
  4246.     }
  4247.   else
  4248.     {
  4249.       top_node_seen = 1;
  4250.  
  4251.       /* It is an error to use @top before you have used @node. */
  4252.       if (!tag_table)
  4253.     {
  4254.       char *top_name;
  4255.  
  4256.       get_rest_of_line (&top_name);
  4257.       free (top_name);
  4258.       line_error ("%ctop used before %cnode, defaulting to %s",
  4259.               COMMAND_PREFIX, COMMAND_PREFIX, top_name);
  4260.       execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
  4261.       return;
  4262.     }
  4263.  
  4264.       cm_unnumbered ();
  4265.  
  4266.       /* The most recently defined node is the top node. */
  4267.       tag_table->flags |= IS_TOP;
  4268.  
  4269.       /* Now set the logical hierarchical level of the Top node. */
  4270.       {
  4271.     int orig_offset = input_text_offset;
  4272.  
  4273.     input_text_offset = search_forward (node_search_string, orig_offset);
  4274.  
  4275.     if (input_text_offset > 0)
  4276.       {
  4277.         int this_section;
  4278.  
  4279.         /* We have encountered a non-top node, so mark that one exists. */
  4280.         non_top_node_seen = 1;
  4281.  
  4282.         /* Move to the end of this line, and find out what the
  4283.            sectioning command is here. */
  4284.         while (input_text[input_text_offset] != '\n')
  4285.           input_text_offset++;
  4286.  
  4287.         if (input_text_offset < size_of_input_text)
  4288.           input_text_offset++;
  4289.  
  4290.         this_section = what_section (input_text + input_text_offset);
  4291.  
  4292.         /* If we found a sectioning command, then give the top section
  4293.            a level of this section - 1. */
  4294.         if (this_section != -1)
  4295.           set_top_section_level (this_section - 1);
  4296.       }
  4297.     input_text_offset = orig_offset;
  4298.       }
  4299.     }
  4300. }
  4301.  
  4302. /* Organized by level commands.  That is, "*" == chapter, "=" == section. */
  4303. char *scoring_characters = "*=-.";
  4304.  
  4305. static void
  4306. sectioning_underscore (char *command)
  4307. {
  4308.   char character;
  4309.   char *temp;
  4310.   int level;
  4311.  
  4312.   temp = (char *)xmalloc (2 + strlen (command));
  4313.   temp[0] = COMMAND_PREFIX;
  4314.   strcpy (&temp[1], command);
  4315.   level = what_section (temp);
  4316.   free (temp);
  4317.   level -= 2;
  4318.  
  4319.   if (level < 0)
  4320.     level = 0;
  4321.  
  4322.   character = scoring_characters[level];
  4323.  
  4324.   insert_and_underscore (character);
  4325. }
  4326.  
  4327. /* The remainder of the text on this line is a chapter heading. */
  4328. static void
  4329. cm_chapter (void)
  4330. {
  4331.   sectioning_underscore ("chapter");
  4332. }
  4333.  
  4334. /* The remainder of the text on this line is a section heading. */
  4335. static void
  4336. cm_section (void)
  4337. {
  4338.   sectioning_underscore ("section");
  4339. }
  4340.  
  4341. /* The remainder of the text on this line is a subsection heading. */
  4342. static void
  4343. cm_subsection (void)
  4344. {
  4345.   sectioning_underscore ("subsection");
  4346. }
  4347.  
  4348. /* The remainder of the text on this line is a subsubsection heading. */
  4349. static void
  4350. cm_subsubsection (void)
  4351. {
  4352.   sectioning_underscore ("subsubsection");
  4353. }
  4354.  
  4355. /* The remainder of the text on this line is an unnumbered heading. */
  4356. static void
  4357. cm_unnumbered (void)
  4358. {
  4359.   cm_chapter ();
  4360. }
  4361.  
  4362. /* The remainder of the text on this line is an unnumbered section heading. */
  4363. static void
  4364. cm_unnumberedsec (void)
  4365. {
  4366.   cm_section ();
  4367. }
  4368.  
  4369. /* The remainder of the text on this line is an unnumbered
  4370.    subsection heading. */
  4371. static void
  4372. cm_unnumberedsubsec (void)
  4373. {
  4374.   cm_subsection ();
  4375. }
  4376.  
  4377. /* The remainder of the text on this line is an unnumbered
  4378.    subsubsection heading. */
  4379. static void
  4380. cm_unnumberedsubsubsec (void)
  4381. {
  4382.   cm_subsubsection ();
  4383. }
  4384.  
  4385. /* The remainder of the text on this line is an appendix heading. */
  4386. static void
  4387. cm_appendix (void)
  4388. {
  4389.   cm_chapter ();
  4390. }
  4391.  
  4392. /* The remainder of the text on this line is an appendix section heading. */
  4393. static void
  4394. cm_appendixsec (void)
  4395. {
  4396.   cm_section ();
  4397. }
  4398.  
  4399. /* The remainder of the text on this line is an appendix subsection heading. */
  4400. static void
  4401. cm_appendixsubsec (void)
  4402. {
  4403.   cm_subsection ();
  4404. }
  4405.  
  4406. /* The remainder of the text on this line is an appendix
  4407.    subsubsection heading. */
  4408. static void
  4409. cm_appendixsubsubsec (void)
  4410. {
  4411.   cm_subsubsection ();
  4412. }
  4413.  
  4414. /* Compatibility functions substitute for chapter, section, etc. */
  4415. static void
  4416. cm_majorheading (void)
  4417. {
  4418.   cm_chapheading ();
  4419. }
  4420.  
  4421. static void
  4422. cm_chapheading (void)
  4423. {
  4424.   cm_chapter ();
  4425. }
  4426.  
  4427. static void
  4428. cm_heading (void)
  4429. {
  4430.   cm_section ();
  4431. }
  4432.  
  4433. static void
  4434. cm_subheading (void)
  4435. {
  4436.   cm_subsection ();
  4437. }
  4438.  
  4439. static void
  4440. cm_subsubheading (void)
  4441. {
  4442.   cm_subsubsection ();
  4443. }
  4444.  
  4445.  
  4446. /* **************************************************************** */
  4447. /*                                    */
  4448. /*           Adding nodes, and making tags            */
  4449. /*                                    */
  4450. /* **************************************************************** */
  4451.  
  4452. /* Start a new tag table. */
  4453. static void
  4454. init_tag_table (void)
  4455. {
  4456.   while (tag_table != (TAG_ENTRY *) NULL)
  4457.     {
  4458.       TAG_ENTRY *temp = tag_table;
  4459.       free (temp->node);
  4460.       free (temp->prev);
  4461.       free (temp->next);
  4462.       free (temp->up);
  4463.       tag_table = tag_table->next_ent;
  4464.       free (temp);
  4465.     }
  4466. }
  4467.  
  4468. static void
  4469. write_tag_table (void)
  4470. {
  4471.   write_tag_table_internal (0);    /* Not indirect. */
  4472. }
  4473.  
  4474. static void
  4475. write_tag_table_indirect (void)
  4476. {
  4477.   write_tag_table_internal (1);
  4478. }
  4479.  
  4480. /* Write out the contents of the existing tag table.
  4481.    INDIRECT_P says how to format the output. */
  4482. static void
  4483. write_tag_table_internal (int indirect_p)
  4484. {
  4485.   TAG_ENTRY *node = tag_table;
  4486.   int old_indent = no_indent;
  4487.  
  4488.   no_indent = 1;
  4489.   filling_enabled = 0;
  4490.   must_start_paragraph = 0;
  4491.   close_paragraph ();
  4492.  
  4493.   if (!indirect_p)
  4494.     {
  4495.       no_indent = 1;
  4496.       insert ('\n');
  4497.     }
  4498.  
  4499.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  4500.  
  4501.   while (node != (TAG_ENTRY *) NULL)
  4502.     {
  4503.       execute_string ("Node: %s", node->node);
  4504.       add_word_args ("\177%d\n", node->position);
  4505.       node = node->next_ent;
  4506.     }
  4507.  
  4508.   add_word ("\037\nEnd Tag Table\n");
  4509.   flush_output ();
  4510.   no_indent = old_indent;
  4511. }
  4512.  
  4513. static char *
  4514. get_node_token (void)
  4515. {
  4516.   char *string;
  4517.  
  4518.   get_until_in_line (",", &string);
  4519.  
  4520.   if (curchar () == ',')
  4521.     input_text_offset++;
  4522.  
  4523.   canon_white (string);
  4524.  
  4525.   /* Force all versions of "top" to be "Top". */
  4526.   normalize_node_name (string);
  4527.  
  4528.   return (string);
  4529. }
  4530.  
  4531. /* Convert "top" and friends into "Top". */
  4532. static void
  4533. normalize_node_name (char *string)
  4534. {
  4535.   if (strcasecmp (string, "Top") == 0)
  4536.     strcpy (string, "Top");
  4537. }
  4538.  
  4539. /* Look up NAME in the tag table, and return the associated
  4540.    tag_entry.  If the node is not in the table return NULL. */
  4541. static TAG_ENTRY *
  4542. find_node (char *name)
  4543. {
  4544.   TAG_ENTRY *tag = tag_table;
  4545.  
  4546.   while (tag != (TAG_ENTRY *) NULL)
  4547.     {
  4548.       if (strcmp (tag->node, name) == 0)
  4549.     return (tag);
  4550.       tag = tag->next_ent;
  4551.     }
  4552.   return ((TAG_ENTRY *) NULL);
  4553. }
  4554.  
  4555. /* Remember NODE and associates. */
  4556. static void
  4557. remember_node (char *node, char *prev, char *next, char *up, int position,
  4558.            int line_no, int no_warn)
  4559. {
  4560.   /* Check for existence of this tag already. */
  4561.   if (validating)
  4562.     {
  4563.       register TAG_ENTRY *tag = find_node (node);
  4564.       if (tag)
  4565.     {
  4566.       line_error ("Node `%s' multiply defined (%d is first definition)",
  4567.               node, tag->line_no);
  4568.       return;
  4569.     }
  4570.     }
  4571.  
  4572.   /* First, make this the current node. */
  4573.   current_node = node;
  4574.  
  4575.   /* Now add it to the list. */
  4576.   {
  4577.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  4578.     new->node = node;
  4579.     new->prev = prev;
  4580.     new->next = next;
  4581.     new->up = up;
  4582.     new->position = position;
  4583.     new->line_no = line_no;
  4584.     new->filename = node_filename;
  4585.     new->touched = 0;        /* not yet referenced. */
  4586.     new->flags = 0;
  4587.     if (no_warn)
  4588.       new->flags |= NO_WARN;
  4589.     new->next_ent = tag_table;
  4590.     tag_table = new;
  4591.   }
  4592. }
  4593.  
  4594. /* The order is: nodename, nextnode, prevnode, upnode.
  4595.    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
  4596.    You must follow a node command which has those fields defaulted
  4597.    with a sectioning command (e.g. @chapter) giving the "level" of that node.
  4598.    It is an error not to do so.
  4599.    The defaults come from the menu in this node's parent. */
  4600. static void
  4601. cm_node (void)
  4602. {
  4603.   char *node, *prev, *next, *up;
  4604.   int new_node_pos, defaulting, this_section, no_warn = 0;
  4605.   extern int already_outputting_pending_notes;
  4606.  
  4607.   if (strcmp (command, "nwnode") == 0)
  4608.     no_warn = 1;
  4609.  
  4610.   /* Get rid of unmatched brace arguments from previous commands. */
  4611.   discard_braces ();
  4612.  
  4613.   /* There also might be insertions left lying around that haven't been
  4614.      ended yet.  Do that also. */
  4615.   discard_insertions ();
  4616.  
  4617.   if (!already_outputting_pending_notes)
  4618.     {
  4619.       close_paragraph ();
  4620.       output_pending_notes ();
  4621.       free_pending_notes ();
  4622.     }
  4623.  
  4624.   filling_enabled = indented_fill = 0;
  4625.   new_node_pos = output_position;
  4626.   current_footnote_number = 1;
  4627.  
  4628. #if defined (HAVE_MACROS)
  4629.   if (macro_expansion_output_stream)
  4630.     append_to_expansion_output (input_text_offset + 1);
  4631. #endif /* HAVE_MACROS */
  4632.  
  4633.   node = get_node_token ();
  4634.   next = get_node_token ();
  4635.   prev = get_node_token ();
  4636.   up = get_node_token ();
  4637.  
  4638. #if defined (HAVE_MACROS)
  4639.   if (macro_expansion_output_stream)
  4640.     remember_itext (input_text, input_text_offset);
  4641. #endif /* HAVE_MACROS */
  4642.  
  4643.   no_indent = 1;
  4644.   if (!no_headers)
  4645.     {
  4646.       add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
  4647.  
  4648. #if defined (HAVE_MACROS)
  4649.       if (macro_expansion_output_stream)
  4650.     me_execute_string (node);
  4651.       else
  4652. #endif /* HAVE_MACROS */
  4653.       execute_string ("%s", node);
  4654.       filling_enabled = indented_fill = 0;
  4655.     }
  4656.  
  4657.   /* Check for defaulting of this node's next, prev, and up fields. */
  4658.   defaulting = ((strlen (next) == 0) &&
  4659.         (strlen (prev) == 0) &&
  4660.         (strlen (up) == 0));
  4661.  
  4662.   this_section = what_section (input_text + input_text_offset);
  4663.  
  4664.   /* If we are defaulting, then look at the immediately following
  4665.      sectioning command (error if none) to determine the node's
  4666.      level.  Find the node that contains the menu mentioning this node
  4667.      that is one level up (error if not found).  That node is the "Up"
  4668.      of this node.  Default the "Next" and "Prev" from the menu. */
  4669.   if (defaulting)
  4670.     {
  4671.       NODE_REF *last_ref = (NODE_REF *)NULL;
  4672.       NODE_REF *ref = node_references;
  4673.  
  4674.       if ((this_section < 0) && (strcmp (node, "Top") != 0))
  4675.     {
  4676.       char *polite_section_name = "top";
  4677.       int i;
  4678.  
  4679.       for (i = 0; section_alist[i].name; i++)
  4680.         if (section_alist[i].level == current_section + 1)
  4681.           {
  4682.         polite_section_name = section_alist[i].name;
  4683.         break;
  4684.           }
  4685.  
  4686.       line_error
  4687.         ("Node `%s' requires a sectioning command (e.g. %c%s)",
  4688.          node, COMMAND_PREFIX, polite_section_name);
  4689.     }
  4690.       else
  4691.     {
  4692.       if (strcmp (node, "Top") == 0)
  4693.         {
  4694.           /* Default the NEXT pointer to be the first menu item in
  4695.          this node, if there is a menu in this node.  We have to
  4696.          try very hard to find the menu, as it may be obscured
  4697.          by execution_strings which are on the filestack.  For
  4698.          every member of the filestack which has a FILENAME
  4699.          member which is identical to the current INPUT_FILENAME,
  4700.          search forward from that offset. */
  4701.           int saved_input_text_offset = input_text_offset;
  4702.           int saved_size_of_input_text = size_of_input_text;
  4703.           char *saved_input_text = input_text;
  4704.           FSTACK *next_file = filestack;
  4705.  
  4706.           int orig_offset, orig_size;
  4707.  
  4708.           /* No matter what, make this file point back at `(dir)'. */
  4709.           free (up);   up = strdup ("(dir)");
  4710.  
  4711.           while (1)
  4712.         {
  4713.           orig_offset = input_text_offset;
  4714.           orig_size =
  4715.             search_forward (node_search_string, orig_offset);
  4716.  
  4717.           if (orig_size < 0)
  4718.             orig_size = size_of_input_text;
  4719.  
  4720.           input_text_offset =
  4721.             search_forward (menu_search_string, orig_offset);
  4722.  
  4723.           if (input_text_offset > -1)
  4724.             {
  4725.               char *nodename_from_menu = (char *)NULL;
  4726.  
  4727.               input_text_offset =
  4728.             search_forward ("\n* ", input_text_offset);
  4729.  
  4730.               if (input_text_offset != -1)
  4731.             nodename_from_menu = glean_node_from_menu (0);
  4732.  
  4733.               if (nodename_from_menu)
  4734.             {
  4735.               free (next); next = nodename_from_menu;
  4736.               break;
  4737.             }
  4738.             }
  4739.  
  4740.           /* We got here, so it hasn't been found yet.  Try
  4741.              the next file on the filestack if there is one. */
  4742.           if (next_file &&
  4743.               (strcmp (next_file->filename, input_filename) == 0))
  4744.             {
  4745.               input_text = next_file->text;
  4746.               input_text_offset = next_file->offset;
  4747.               size_of_input_text = next_file->size;
  4748.               next_file = next_file->next;
  4749.             }
  4750.           else
  4751.             {
  4752.               /* No more input files to check. */
  4753.               break;
  4754.             }
  4755.         }
  4756.  
  4757.           input_text = saved_input_text;
  4758.           input_text_offset = saved_input_text_offset;
  4759.           size_of_input_text = saved_size_of_input_text;
  4760.         }
  4761.     }
  4762.  
  4763.       /* Fix the level of the menu references in the Top node, iff it
  4764.      was declared with @top, and no subsequent reference was found. */
  4765.       if (top_node_seen && !non_top_node_seen)
  4766.     {
  4767.       /* Then this is the first non-@top node seen. */
  4768.       int level;
  4769.  
  4770.       level = set_top_section_level (this_section - 1);
  4771.       non_top_node_seen = 1;
  4772.  
  4773.       while (ref)
  4774.         {
  4775.           if (ref->section == level)
  4776.         ref->section = this_section - 1;
  4777.           ref = ref->next;
  4778.         }
  4779.  
  4780.       ref = node_references;
  4781.     }
  4782.  
  4783.       while (ref)
  4784.     {
  4785.       if (ref->section == (this_section - 1) &&
  4786.           ref->type == menu_reference &&
  4787.           strcmp (ref->node, node) == 0)
  4788.         {
  4789.           char *containing_node = ref->containing_node;
  4790.  
  4791.           free (up);
  4792.           up = strdup (containing_node);
  4793.  
  4794.           if (last_ref &&
  4795.           last_ref->type == menu_reference &&
  4796.           (strcmp (last_ref->containing_node,
  4797.                containing_node) == 0))
  4798.         {
  4799.           free (next);
  4800.           next = strdup (last_ref->node);
  4801.         }
  4802.  
  4803.           while ((ref->section == this_section - 1) &&
  4804.              (ref->next) &&
  4805.              (ref->next->type != menu_reference))
  4806.         ref = ref->next;
  4807.  
  4808.           if (ref->next && ref->type == menu_reference &&
  4809.           (strcmp (ref->next->containing_node,
  4810.                containing_node) == 0))
  4811.         {
  4812.           free (prev);
  4813.           prev = strdup (ref->next->node);
  4814.         }
  4815.           else if (!ref->next &&
  4816.                strcasecmp (ref->containing_node, "Top") == 0)
  4817.         {
  4818.           free (prev);
  4819.           prev = strdup (ref->containing_node);
  4820.         }
  4821.           break;
  4822.         }
  4823.       last_ref = ref;
  4824.       ref = ref->next;
  4825.     }
  4826.     }
  4827.  
  4828. #if defined (HAVE_MACROS)
  4829.   /* Insert the correct args if we are expanding macros, and the node's
  4830.      pointers weren't defaulted. */
  4831.   if (macro_expansion_output_stream && !defaulting)
  4832.     {
  4833.       char *temp;
  4834.       int op_orig = output_paragraph_offset;
  4835.  
  4836.       temp = (char *)xmalloc (3 + strlen (next));
  4837.       sprintf (temp, ", %s", next);
  4838.       me_execute_string (temp);
  4839.       free (temp);
  4840.  
  4841.       temp = (char *)xmalloc (3 + strlen (prev));
  4842.       sprintf (temp, ", %s", prev);
  4843.       me_execute_string (temp);
  4844.       free (temp);
  4845.  
  4846.       temp = (char *)xmalloc (4 + strlen (up));
  4847.       sprintf (temp, ", %s", up);
  4848.       me_execute_string (temp);
  4849.       free (temp);
  4850.  
  4851.       output_paragraph_offset = op_orig;
  4852.     }
  4853. #endif /* HAVE_MACROS */
  4854.  
  4855.   if (!no_headers)
  4856.     {
  4857. #if defined (HAVE_MACROS)
  4858.       if (macro_expansion_output_stream)
  4859.     me_inhibit_expansion++;
  4860. #endif /* HAVE_MACROS */
  4861.  
  4862.       if (*next)
  4863.     {
  4864.       execute_string (",  Next: %s", next);
  4865.       filling_enabled = indented_fill = 0;
  4866.     }
  4867.  
  4868.       if (*prev)
  4869.     {
  4870.       execute_string (",  Prev: %s", prev);
  4871.       filling_enabled = indented_fill = 0;
  4872.     }
  4873.  
  4874.       if (*up)
  4875.     {
  4876.       execute_string (",  Up: %s", up);
  4877.       filling_enabled = indented_fill = 0;
  4878.     }
  4879. #if defined (HAVE_MACROS)
  4880.       if (macro_expansion_output_stream)
  4881.     me_inhibit_expansion--;
  4882. #endif /* HAVE_MACROS */
  4883.     }
  4884.  
  4885.   close_paragraph ();
  4886.   no_indent = 0;
  4887.  
  4888.   if (!*node)
  4889.     {
  4890.       line_error ("No node name specified for `%c%s' command",
  4891.           COMMAND_PREFIX, command);
  4892.       free (node);
  4893.       free (next);
  4894.       free (prev);
  4895.       free (up);
  4896.     }
  4897.   else
  4898.     {
  4899.       if (!*next) { free (next); next = (char *)NULL; }
  4900.       if (!*prev) { free (prev); prev = (char *)NULL; }
  4901.       if (!*up) { free (up); up = (char *)NULL; }
  4902.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  4903.     }
  4904.  
  4905.   /* Change the section only if there was a sectioning command. */
  4906.   if (this_section >= 0)
  4907.     current_section = this_section;
  4908.  
  4909.   filling_enabled = 1;
  4910. }
  4911.  
  4912. /* Validation of an info file.
  4913.    Scan through the list of tag entrys touching the Prev, Next, and Up
  4914.    elements of each.  It is an error not to be able to touch one of them,
  4915.    except in the case of external node references, such as "(DIR)".
  4916.  
  4917.    If the Prev is different from the Up,
  4918.    then the Prev node must have a Next pointing at this node.
  4919.  
  4920.    Every node except Top must have an Up.
  4921.    The Up node must contain some sort of reference, other than a Next,
  4922.    to this node.
  4923.  
  4924.    If the Next is different from the Next of the Up,
  4925.    then the Next node must have a Prev pointing at this node. */
  4926. static void
  4927. validate_file (TAG_ENTRY *tag_table)
  4928. {
  4929.   char *old_input_filename = input_filename;
  4930.   TAG_ENTRY *tags = tag_table;
  4931.  
  4932.   while (tags != (TAG_ENTRY *) NULL)
  4933.     {
  4934.       register TAG_ENTRY *temp_tag;
  4935.  
  4936.       input_filename = tags->filename;
  4937.       line_number = tags->line_no;
  4938.  
  4939.       /* If this is a "no warn" node, don't validate it in any way. */
  4940.       if (tags->flags & NO_WARN)
  4941.     {
  4942.       tags = tags->next_ent;
  4943.       continue;
  4944.     }
  4945.  
  4946.       /* If this node has a Next, then make sure that the Next exists. */
  4947.       if (tags->next)
  4948.     {
  4949.       validate (tags->next, tags->line_no, "Next");
  4950.  
  4951.       /* If the Next node exists, and there is no Up, then make
  4952.          sure that the Prev of the Next points back. */
  4953.       if ((temp_tag = find_node (tags->next)))
  4954.         {
  4955.           char *prev;
  4956.  
  4957.           if (temp_tag->flags & NO_WARN)
  4958.         {
  4959.           /* Do nothing if we aren't supposed to issue warnings
  4960.              about this node. */
  4961.         }
  4962.           else
  4963.         {
  4964.           prev = temp_tag->prev;
  4965.           if (!prev || (strcmp (prev, tags->node) != 0))
  4966.             {
  4967.               line_error ("Node `%s''s Next field not pointed back to",
  4968.                   tags->node);
  4969.               line_number = temp_tag->line_no;
  4970.               input_filename = temp_tag->filename;
  4971.               line_error
  4972.             ("This node (`%s') is the one with the bad `Prev'",
  4973.              temp_tag->node);
  4974.               input_filename = tags->filename;
  4975.               line_number = tags->line_no;
  4976.               temp_tag->flags |= PREV_ERROR;
  4977.             }
  4978.         }
  4979.         }
  4980.     }
  4981.  
  4982.       /* Validate the Prev field if there is one, and we haven't already
  4983.      complained about it in some way.  You don't have to have a Prev
  4984.      field at this stage. */
  4985.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  4986.     {
  4987.       int valid = validate (tags->prev, tags->line_no, "Prev");
  4988.  
  4989.       if (!valid)
  4990.         tags->flags |= PREV_ERROR;
  4991.       else
  4992.         {
  4993.           /* If the Prev field is not the same as the Up field,
  4994.          then the node pointed to by the Prev field must have
  4995.          a Next field which points to this node. */
  4996.           if (tags->up && (strcmp (tags->prev, tags->up) != 0))
  4997.         {
  4998.           temp_tag = find_node (tags->prev);
  4999.  
  5000.           /* If we aren't supposed to issue warnings about the
  5001.              target node, do nothing. */
  5002.           if (!temp_tag || (temp_tag->flags & NO_WARN))
  5003.             {
  5004.               /* Do nothing. */
  5005.             }
  5006.           else
  5007.             {
  5008.               if (!temp_tag->next ||
  5009.               (strcmp (temp_tag->next, tags->node) != 0))
  5010.             {
  5011.               line_error
  5012.                 ("Node `%s''s Prev field not pointed back to",
  5013.                  tags->node);
  5014.               line_number = temp_tag->line_no;
  5015.               input_filename = temp_tag->filename;
  5016.               line_error
  5017.                 ("This node (`%s') is the one with the bad `Next'",
  5018.                  temp_tag->node);
  5019.               input_filename = tags->filename;
  5020.               line_number = tags->line_no;
  5021.               temp_tag->flags |= NEXT_ERROR;
  5022.             }
  5023.             }
  5024.         }
  5025.         }
  5026.     }
  5027.  
  5028.       if (!tags->up && (strcasecmp (tags->node, "Top") != 0))
  5029.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  5030.       else if (tags->up)
  5031.     {
  5032.       int valid = validate (tags->up, tags->line_no, "Up");
  5033.  
  5034.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  5035.          or note pointing at X, if Y isn't of the form "(Y)". */
  5036.       if (valid && *tags->up != '(')
  5037.         {
  5038.           NODE_REF *nref, *tref, *list;
  5039.  
  5040.           tref = (NODE_REF *) NULL;
  5041.           list = node_references;
  5042.  
  5043.           for (;;)
  5044.         {
  5045.           if (!(nref = find_node_reference (tags->node, list)))
  5046.             break;
  5047.  
  5048.           if (strcmp (nref->containing_node, tags->up) == 0)
  5049.             {
  5050.               if (nref->type != menu_reference)
  5051.             {
  5052.               tref = nref;
  5053.               list = nref->next;
  5054.             }
  5055.               else
  5056.             break;
  5057.             }
  5058.           list = nref->next;
  5059.         }
  5060.  
  5061.           if (!nref)
  5062.         {
  5063.           temp_tag = find_node (tags->up);
  5064.           line_number = temp_tag->line_no;
  5065.           input_filename = temp_tag->filename;
  5066.           if (!tref)
  5067.             line_error (
  5068. "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  5069.                 tags->node, tags->up, tags->up, tags->node);
  5070.           line_number = tags->line_no;
  5071.           input_filename = tags->filename;
  5072.         }
  5073.         }
  5074.     }
  5075.       tags = tags->next_ent;
  5076.     }
  5077.  
  5078.   validate_other_references (node_references);
  5079.   /* We have told the user about the references which didn't exist.
  5080.      Now tell him about the nodes which aren't referenced. */
  5081.  
  5082.   tags = tag_table;
  5083.   while (tags != (TAG_ENTRY *) NULL)
  5084.     {
  5085.       /* If this node is a "no warn" node, do nothing. */
  5086.       if (tags->flags & NO_WARN)
  5087.     {
  5088.       tags = tags->next_ent;
  5089.       continue;
  5090.     }
  5091.  
  5092.       /* Special hack.  If the node in question appears to have
  5093.          been referenced more than REFERENCE_WARNING_LIMIT times,
  5094.          give a warning. */
  5095.       if (tags->touched > reference_warning_limit)
  5096.     {
  5097.       input_filename = tags->filename;
  5098.       line_number = tags->line_no;
  5099.       warning ("Node `%s' has been referenced %d times",
  5100.            tags->node, tags->touched);
  5101.     }
  5102.  
  5103.       if (tags->touched == 0)
  5104.     {
  5105.       input_filename = tags->filename;
  5106.       line_number = tags->line_no;
  5107.  
  5108.       /* Notice that the node "Top" is special, and doesn't have to
  5109.          be referenced. */
  5110.       if (strcasecmp (tags->node, "Top") != 0)
  5111.         warning ("Unreferenced node `%s'", tags->node);
  5112.     }
  5113.       tags = tags->next_ent;
  5114.     }
  5115.   input_filename = old_input_filename;
  5116. }
  5117.  
  5118. /* Return 1 if tag correctly validated, or 0 if not. */
  5119. static int
  5120. validate (char *tag, int line, char *label)
  5121. {
  5122.   TAG_ENTRY *result;
  5123.  
  5124.   /* If there isn't a tag to verify, or if the tag is in another file,
  5125.      then it must be okay. */
  5126.   if (!tag || !*tag || *tag == '(')
  5127.     return (1);
  5128.  
  5129.   /* Otherwise, the tag must exist. */
  5130.   result = find_node (tag);
  5131.  
  5132.   if (!result)
  5133.     {
  5134.       line_number = line;
  5135.       line_error (
  5136. "Validation error.  `%s' field points to node `%s', which doesn't exist",
  5137.           label, tag);
  5138.       return (0);
  5139.     }
  5140.   result->touched++;
  5141.   return (1);
  5142. }
  5143.  
  5144. /* Split large output files into a series of smaller files.  Each file
  5145.    is pointed to in the tag table, which then gets written out as the
  5146.    original file.  The new files have the same name as the original file
  5147.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  5148.    in any single split file. */
  5149. static void
  5150. split_file (char *filename, int size)
  5151. {
  5152.   char *root_filename, *root_pathname;
  5153.   char *the_file;
  5154.   struct stat fileinfo;
  5155.   long file_size;
  5156.   char *the_header;
  5157.   int header_size;
  5158.  
  5159.   /* Can only do this to files with tag tables. */
  5160.   if (!tag_table)
  5161.     return;
  5162.  
  5163.   if (size == 0)
  5164.     size = DEFAULT_SPLIT_SIZE;
  5165.  
  5166.   if ((stat (filename, &fileinfo) != 0) ||
  5167.       (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
  5168.     return;
  5169.   file_size = (long) fileinfo.st_size;
  5170.  
  5171.   the_file = find_and_load (filename);
  5172.   if (!the_file)
  5173.     return;
  5174.  
  5175.   root_filename = filename_part (filename);
  5176.   root_pathname = pathname_part (filename);
  5177.  
  5178.   if (!root_pathname)
  5179.     root_pathname = strdup ("");
  5180.  
  5181.   /* Start splitting the file.  Walk along the tag table
  5182.      outputting sections of the file.  When we have written
  5183.      all of the nodes in the tag table, make the top-level
  5184.      pointer file, which contains indirect pointers and
  5185.      tags for the nodes. */
  5186.   {
  5187.     int which_file = 1;
  5188.     TAG_ENTRY *tags = tag_table;
  5189.     char *indirect_info = (char *)NULL;
  5190.  
  5191.     /* Remember the `header' of this file.  The first tag in the file is
  5192.        the bottom of the header; the top of the file is the start. */
  5193.     the_header = (char *)xmalloc (1 + (header_size = tags->position));
  5194.     memcpy (the_header, the_file, header_size);
  5195.  
  5196.     while (tags)
  5197.       {
  5198.     int file_top, file_bot, limit;
  5199.  
  5200.     /* Have to include the Control-_. */
  5201.     file_top = file_bot = tags->position;
  5202.     limit = file_top + size;
  5203.  
  5204.     /* If the rest of this file is only one node, then
  5205.        that is the entire subfile. */
  5206.     if (!tags->next_ent)
  5207.       {
  5208.         int i = tags->position + 1;
  5209.         char last_char = the_file[i];
  5210.  
  5211.         while (i < file_size)
  5212.           {
  5213.         if ((the_file[i] == '\037') &&
  5214.             ((last_char == '\n') ||
  5215.              (last_char == '\014')))
  5216.           break;
  5217.         else
  5218.           last_char = the_file[i];
  5219.         i++;
  5220.           }
  5221.         file_bot = i;
  5222.         tags = tags->next_ent;
  5223.         goto write_region;
  5224.       }
  5225.  
  5226.     /* Otherwise, find the largest number of nodes that can fit in
  5227.        this subfile. */
  5228.     for (; tags; tags = tags->next_ent)
  5229.       {
  5230.         if (!tags->next_ent)
  5231.           {
  5232.         /* This entry is the last node.  Search forward for the end
  5233.                of this node, and that is the end of this file. */
  5234.         int i = tags->position + 1;
  5235.         char last_char = the_file[i];
  5236.  
  5237.         while (i < file_size)
  5238.           {
  5239.             if ((the_file[i] == '\037') &&
  5240.             ((last_char == '\n') ||
  5241.              (last_char == '\014')))
  5242.               break;
  5243.             else
  5244.               last_char = the_file[i];
  5245.             i++;
  5246.           }
  5247.         file_bot = i;
  5248.  
  5249.         if (file_bot < limit)
  5250.           {
  5251.             tags = tags->next_ent;
  5252.             goto write_region;
  5253.           }
  5254.         else
  5255.           {
  5256.             /* Here we want to write out everything before the last
  5257.                node, and then write the last node out in a file
  5258.                by itself. */
  5259.             file_bot = tags->position;
  5260.             goto write_region;
  5261.           }
  5262.           }
  5263.  
  5264.         if (tags->next_ent->position > limit)
  5265.           {
  5266.         if (tags->position == file_top)
  5267.           tags = tags->next_ent;
  5268.  
  5269.         file_bot = tags->position;
  5270.  
  5271.           write_region:
  5272.         {
  5273.           int fd;
  5274.           char *split_filename;
  5275.  
  5276.           split_filename = (char *) xmalloc
  5277.             (10 + strlen (root_pathname) + strlen (root_filename));
  5278.           sprintf
  5279.             (split_filename,
  5280. #ifdef MSDOS
  5281.              "%s%s.%d", root_pathname, root_filename, which_file);
  5282. #else /* !MSDOS */
  5283.              "%s%s-%d", root_pathname, root_filename, which_file);
  5284. #endif /* !MSDOS */
  5285.  
  5286.           fd = open
  5287.             (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  5288.  
  5289.           if ((fd < 0) ||
  5290.               (write (fd, the_header, header_size) != header_size) ||
  5291.               (write (fd, the_file + file_top, file_bot - file_top)
  5292.                != (file_bot - file_top)) ||
  5293.               ((close (fd)) < 0))
  5294.             {
  5295.               perror (split_filename);
  5296.               if (fd != -1)
  5297.             close (fd);
  5298.               exit (FATAL);
  5299.             }
  5300.  
  5301.           if (!indirect_info)
  5302.             {
  5303.               indirect_info = the_file + file_top;
  5304.               sprintf (indirect_info, "\037\nIndirect:\n");
  5305.               indirect_info += strlen (indirect_info);
  5306.             }
  5307.  
  5308. #ifdef MSDOS
  5309.           sprintf (indirect_info, "%s.%d: %d\n",
  5310. #else
  5311.           sprintf (indirect_info, "%s-%d: %d\n",
  5312. #endif
  5313.                root_filename, which_file, file_top);
  5314.  
  5315.           free (split_filename);
  5316.           indirect_info += strlen (indirect_info);
  5317.           which_file++;
  5318.           break;
  5319.         }
  5320.           }
  5321.       }
  5322.       }
  5323.  
  5324.     /* We have sucessfully created the subfiles.  Now write out the
  5325.        original again.  We must use `output_stream', or
  5326.        write_tag_table_indirect () won't know where to place the output. */
  5327.     output_stream = fopen (filename, "w");
  5328.     if (!output_stream)
  5329.       {
  5330.     perror (filename);
  5331.     exit (FATAL);
  5332.       }
  5333.  
  5334.     {
  5335.       int distance = indirect_info - the_file;
  5336.       fwrite (the_file, 1, distance, output_stream);
  5337.  
  5338.       /* Inhibit newlines. */
  5339.       paragraph_is_open = 0;
  5340.  
  5341.       write_tag_table_indirect ();
  5342.       fclose (output_stream);
  5343.       free (the_header);
  5344.       free (the_file);
  5345.       return;
  5346.     }
  5347.   }
  5348. }
  5349.  
  5350. /* Some menu hacking.  This is used to remember menu references while
  5351.    reading the input file.  After the output file has been written, if
  5352.    validation is on, then we use the contents of NODE_REFERENCES as a
  5353.    list of nodes to validate. */
  5354. static char *
  5355. reftype_type_string (enum reftype type)
  5356. {
  5357.   switch (type)
  5358.     {
  5359.     case menu_reference:
  5360.       return ("Menu");
  5361.     case followed_reference:
  5362.       return ("Followed-Reference");
  5363.     default:
  5364.       return ("Internal-bad-reference-type");
  5365.     }
  5366. }
  5367.  
  5368. /* Remember this node name for later validation use. */
  5369. static void
  5370. remember_node_reference (char *node, int line, enum reftype type)
  5371. {
  5372.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  5373.  
  5374.   temp->next = node_references;
  5375.   temp->node = strdup (node);
  5376.   temp->line_no = line;
  5377.   temp->section = current_section;
  5378.   temp->type = type;
  5379.   temp->containing_node = strdup (current_node);
  5380.   temp->filename = node_filename;
  5381.  
  5382.   node_references = temp;
  5383. }
  5384.  
  5385. static void
  5386. validate_other_references (register NODE_REF *ref_list)
  5387. {
  5388.   char *old_input_filename = input_filename;
  5389.  
  5390.   while (ref_list != (NODE_REF *) NULL)
  5391.     {
  5392.       input_filename = ref_list->filename;
  5393.       validate (ref_list->node, ref_list->line_no,
  5394.         reftype_type_string (ref_list->type));
  5395.       ref_list = ref_list->next;
  5396.     }
  5397.   input_filename = old_input_filename;
  5398. }
  5399.  
  5400. /* Find NODE in REF_LIST. */
  5401. static NODE_REF *
  5402. find_node_reference (char *node, register NODE_REF *ref_list)
  5403. {
  5404.   while (ref_list)
  5405.     {
  5406.       if (strcmp (node, ref_list->node) == 0)
  5407.     break;
  5408.       ref_list = ref_list->next;
  5409.     }
  5410.   return (ref_list);
  5411. }
  5412.  
  5413. static void
  5414. free_node_references (void)
  5415. {
  5416.   register NODE_REF *list, *temp;
  5417.  
  5418.   list = node_references;
  5419.  
  5420.   while (list)
  5421.     {
  5422.       temp = list;
  5423.       free (list->node);
  5424.       free (list->containing_node);
  5425.       list = list->next;
  5426.       free (temp);
  5427.     }
  5428.   node_references = (NODE_REF *) NULL;
  5429. }
  5430.  
  5431.   /* This function gets called at the start of every line while inside of
  5432.      a menu.  It checks to see if the line starts with "* ", and if so,
  5433.      remembers the node reference that this menu refers to.
  5434.      input_text_offset is at the \n just before the line start. */
  5435. #define menu_starter "* "
  5436. static char *
  5437. glean_node_from_menu (int remember_reference)
  5438. {
  5439.   int i, orig_offset = input_text_offset;
  5440.   char *nodename;
  5441.  
  5442.   if (strncmp (&input_text[input_text_offset + 1],
  5443.            menu_starter,
  5444.            strlen (menu_starter)) != 0)
  5445.     return ((char *)NULL);
  5446.   else
  5447.     input_text_offset += strlen (menu_starter) + 1;
  5448.  
  5449.   get_until_in_line (":", &nodename);
  5450.   if (curchar () == ':')
  5451.     input_text_offset++;
  5452.   canon_white (nodename);
  5453.  
  5454.   if (curchar () == ':')
  5455.     goto save_node;
  5456.  
  5457.   free (nodename);
  5458.   get_rest_of_line (&nodename);
  5459.  
  5460.   /* Special hack: If the nodename follows the menu item name,
  5461.      then we have to read the rest of the line in order to find
  5462.      out what the nodename is.  But we still have to read the
  5463.      line later, in order to process any formatting commands that
  5464.      might be present.  So un-count the carriage return that has just
  5465.      been counted. */
  5466.   line_number--;
  5467.  
  5468.   isolate_nodename (nodename);
  5469.  
  5470. save_node:
  5471.   input_text_offset = orig_offset;
  5472.   normalize_node_name (nodename);
  5473.   i = strlen (nodename);
  5474.   if (i && nodename[i - 1] == ':')
  5475.     nodename[i - 1] = '\0';
  5476.  
  5477.   if (remember_reference)
  5478.     {
  5479.       remember_node_reference (nodename, line_number, menu_reference);
  5480.       free (nodename);
  5481.       return ((char *)NULL);
  5482.     }
  5483.   else
  5484.     return (nodename);
  5485. }
  5486.  
  5487. static void
  5488. isolate_nodename (char *nodename)
  5489. {
  5490.   register int i, c;
  5491.   int paren_seen, paren;
  5492.  
  5493.   if (!nodename)
  5494.     return;
  5495.  
  5496.   canon_white (nodename);
  5497.   paren_seen = paren = i = 0;
  5498.  
  5499.   if (*nodename == '.' || !*nodename)
  5500.     {
  5501.       *nodename = '\0';
  5502.       return;
  5503.     }
  5504.  
  5505.   if (*nodename == '(')
  5506.     {
  5507.       paren++;
  5508.       paren_seen++;
  5509.       i++;
  5510.     }
  5511.  
  5512.   for (; (c = nodename[i]); i++)
  5513.     {
  5514.       if (paren)
  5515.     {
  5516.       if (c == '(')
  5517.         paren++;
  5518.       else if (c == ')')
  5519.         paren--;
  5520.  
  5521.       continue;
  5522.     }
  5523.  
  5524.       /* If the character following the close paren is a space, then this
  5525.      node has no more characters associated with it. */
  5526.       if (c == '\t' ||
  5527.       c == '\n' ||
  5528.       c == ','  ||
  5529.       ((paren_seen && nodename[i - 1] == ')') &&
  5530.        (c == ' ' || c == '.')) ||
  5531.       (c == '.' &&
  5532.        ((!nodename[i + 1] ||
  5533.          (cr_or_whitespace (nodename[i + 1])) ||
  5534.          (nodename[i + 1] == ')')))))
  5535.     break;
  5536.     }
  5537.   nodename[i] = '\0';
  5538. }
  5539.  
  5540. static void
  5541. cm_menu (void)
  5542. {
  5543.   begin_insertion (menu);
  5544. }
  5545.  
  5546.  
  5547. /* **************************************************************** */
  5548. /*                                    */
  5549. /*            Cross Reference Hacking                */
  5550. /*                                    */
  5551. /* **************************************************************** */
  5552.  
  5553. static char *
  5554. get_xref_token (void)
  5555. {
  5556.   char *string;
  5557.  
  5558.   get_until_in_braces (",", &string);
  5559.   if (curchar () == ',')
  5560.     input_text_offset++;
  5561.   fix_whitespace (string);
  5562.   return (string);
  5563. }
  5564.  
  5565. int px_ref_flag = 0;        /* Controls initial output string. */
  5566.  
  5567. /* Make a cross reference. */
  5568. static void
  5569. cm_xref (int arg)
  5570. {
  5571.   if (arg == START)
  5572.     {
  5573.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  5574.  
  5575.       arg1 = get_xref_token ();
  5576.       arg2 = get_xref_token ();
  5577.       arg3 = get_xref_token ();
  5578.       arg4 = get_xref_token ();
  5579.       arg5 = get_xref_token ();
  5580.  
  5581.       add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  5582.  
  5583.       if (*arg5 || *arg4)
  5584.     {
  5585.       char *node_name;
  5586.  
  5587.       if (!*arg2)
  5588.         {
  5589.           if (*arg3)
  5590.         node_name = arg3;
  5591.           else
  5592.         node_name = arg1;
  5593.         }
  5594.       else
  5595.         node_name = arg2;
  5596.  
  5597.       execute_string ("%s: (%s)%s", node_name, arg4, arg1);
  5598.       return;
  5599.     }
  5600.       else
  5601.     remember_node_reference (arg1, line_number, followed_reference);
  5602.  
  5603.       if (*arg3)
  5604.     {
  5605.       if (!*arg2)
  5606.         execute_string ("%s: %s", arg3, arg1);
  5607.       else
  5608.         execute_string ("%s: %s", arg2, arg1);
  5609.     }
  5610.       else
  5611.     {
  5612.       if (*arg2)
  5613.         execute_string ("%s: %s", arg2, arg1);
  5614.       else
  5615.         execute_string ("%s::", arg1);
  5616.     }
  5617.  
  5618.       /* Free all of the arguments found. */
  5619.       if (arg1) free (arg1);
  5620.       if (arg2) free (arg2);
  5621.       if (arg3) free (arg3);
  5622.       if (arg4) free (arg4);
  5623.       if (arg5) free (arg5);
  5624.     }
  5625.   else
  5626.     {
  5627.       /* Check to make sure that the next non-whitespace character is either
  5628.          a period or a comma. input_text_offset is pointing at the "}" which
  5629.          ended the xref or pxref command. */
  5630.       int temp = input_text_offset + 1;
  5631.  
  5632.       if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  5633.       output_paragraph[output_paragraph_offset - 1] == ':')
  5634.     return;
  5635.       while (temp < size_of_input_text)
  5636.     {
  5637.       if (cr_or_whitespace (input_text[temp]))
  5638.         temp++;
  5639.       else
  5640.         {
  5641.           if (input_text[temp] == '.' ||
  5642.           input_text[temp] == ',' ||
  5643.           input_text[temp] == '\t')
  5644.         return;
  5645.           else
  5646.         {
  5647.           line_error (
  5648.         "Cross-reference must be terminated with a period or a comma");
  5649.           return;
  5650.         }
  5651.         }
  5652.     }
  5653.     }
  5654. }
  5655.  
  5656. static void
  5657. cm_pxref (int arg)
  5658. {
  5659.   if (arg == START)
  5660.     {
  5661.       px_ref_flag++;
  5662.       cm_xref (arg);
  5663.       px_ref_flag--;
  5664.     }
  5665.   else
  5666.     add_char ('.');
  5667. }
  5668.  
  5669. static void
  5670. cm_inforef (int arg)
  5671. {
  5672.   if (arg == START)
  5673.     {
  5674.       char *node, *pname, *file;
  5675.  
  5676.       node = get_xref_token ();
  5677.       pname = get_xref_token ();
  5678.       file = get_xref_token ();
  5679.  
  5680.       execute_string ("*note %s: (%s)%s", pname, file, node);
  5681.     }
  5682. }
  5683.  
  5684. /* **************************************************************** */
  5685. /*                                    */
  5686. /*            Insertion Command Stubs                */
  5687. /*                                    */
  5688. /* **************************************************************** */
  5689.  
  5690. static void
  5691. cm_quotation (void)
  5692. {
  5693.   begin_insertion (quotation);
  5694. }
  5695.  
  5696. static void
  5697. cm_example (void)
  5698. {
  5699.   begin_insertion (example);
  5700. }
  5701.  
  5702. static void
  5703. cm_smallexample (void)
  5704. {
  5705.   begin_insertion (smallexample);
  5706. }
  5707.  
  5708. static void
  5709. cm_lisp (void)
  5710. {
  5711.   begin_insertion (lisp);
  5712. }
  5713.  
  5714. static void
  5715. cm_smalllisp (void)
  5716. {
  5717.   begin_insertion (smalllisp);
  5718. }
  5719.  
  5720. /* @cartouche/@end cartouche draws box with rounded corners in
  5721.    TeX output.  Right now, just a NOP insertion. */
  5722. static void
  5723. cm_cartouche (void)
  5724. {
  5725.   begin_insertion (cartouche);
  5726. }
  5727.  
  5728. static void
  5729. cm_format (void)
  5730. {
  5731.   begin_insertion (format);
  5732. }
  5733.  
  5734. static void
  5735. cm_display (void)
  5736. {
  5737.   begin_insertion (display);
  5738. }
  5739.  
  5740. static void
  5741. cm_itemize (void)
  5742. {
  5743.   begin_insertion (itemize);
  5744. }
  5745.  
  5746. static void
  5747. cm_enumerate (void)
  5748. {
  5749.   do_enumeration (enumerate, "1");
  5750. }
  5751.  
  5752. /* Start an enumeration insertion of type TYPE.  If the user supplied
  5753.    no argument on the line, then use DEFAULT_STRING as the initial string. */
  5754. static void
  5755. do_enumeration (int type, char *default_string)
  5756. {
  5757.   get_until_in_line (".", &enumeration_arg);
  5758.   canon_white (enumeration_arg);
  5759.  
  5760.   if (!*enumeration_arg)
  5761.     {
  5762.       free (enumeration_arg);
  5763.       enumeration_arg = strdup (default_string);
  5764.     }
  5765.  
  5766.   if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
  5767.     {
  5768.       warning ("%s requires a letter or a digit", insertion_type_pname (type));
  5769.  
  5770.       switch (type)
  5771.     {
  5772.     case enumerate:
  5773.       default_string = "1";
  5774.       break;
  5775.     }
  5776.       enumeration_arg = strdup (default_string);
  5777.     }
  5778.   begin_insertion (type);
  5779. }
  5780.  
  5781. static void
  5782. cm_table (void)
  5783. {
  5784.   begin_insertion (table);
  5785. }
  5786.  
  5787. static void
  5788. cm_ftable (void)
  5789. {
  5790.   begin_insertion (ftable);
  5791. }
  5792.  
  5793. static void
  5794. cm_vtable (void)
  5795. {
  5796.   begin_insertion (vtable);
  5797. }
  5798.  
  5799. static void
  5800. cm_group (void)
  5801. {
  5802.   begin_insertion (group);
  5803. }
  5804.  
  5805. static void
  5806. cm_ifinfo (void)
  5807. {
  5808.   /* begin_insertion (ifinfo); */
  5809.   /* BPW: @ifinfo can cross hierarchies */
  5810.   ifinfo_count++;
  5811.   if (in_menu)
  5812.     discard_until ("\n");  
  5813. }
  5814.  
  5815. /* Begin an insertion where the lines are not filled or indented. */
  5816. static void
  5817. cm_flushleft (void)
  5818. {
  5819.   begin_insertion (flushleft);
  5820. }
  5821.  
  5822. /* Begin an insertion where the lines are not filled, and each line is
  5823.    forced to the right-hand side of the page. */
  5824. static void
  5825. cm_flushright (void)
  5826. {
  5827.   begin_insertion (flushright);
  5828. }
  5829.  
  5830.  
  5831. /* **************************************************************** */
  5832. /*                                    */
  5833. /*              Conditional Handling                */
  5834. /*                                    */
  5835. /* **************************************************************** */
  5836.  
  5837. /* A structure which contains `defined' variables. */
  5838. typedef struct _defines {
  5839.   struct _defines *next;
  5840.   char *name;
  5841.   char *value;
  5842. } DEFINE;
  5843.  
  5844. /* The linked list of `set' defines. */
  5845. DEFINE *defines = (DEFINE *)NULL;
  5846.  
  5847. /* Add NAME to the list of `set' defines. */
  5848. static void
  5849. set (char *name, char *value)
  5850. {
  5851.   DEFINE *temp;
  5852.  
  5853.   for (temp = defines; temp; temp = temp->next)
  5854.     if (strcmp (name, temp->name) == 0)
  5855.       {
  5856.     free (temp->value);
  5857.     temp->value = strdup (value);
  5858.     return;
  5859.       }
  5860.  
  5861.   temp = (DEFINE *)xmalloc (sizeof (DEFINE));
  5862.   temp->next = defines;
  5863.   temp->name = strdup (name);
  5864.   temp->value = strdup (value);
  5865.   defines = temp;
  5866. }
  5867.  
  5868. /* Remove NAME from the list of `set' defines. */
  5869. static void
  5870. clear (char *name)
  5871. {
  5872.   register DEFINE *temp, *last;
  5873.  
  5874.   last = (DEFINE *)NULL;
  5875.   temp = defines;
  5876.  
  5877.   while (temp)
  5878.     {
  5879.       if (strcmp (temp->name, name) == 0)
  5880.     {
  5881.       if (last)
  5882.         last->next = temp->next;
  5883.       else
  5884.         defines = temp->next;
  5885.  
  5886.       free (temp->name);
  5887.       free (temp->value);
  5888.       free (temp);
  5889.       break;
  5890.     }
  5891.       last = temp;
  5892.       temp = temp->next;
  5893.     }
  5894. }
  5895.  
  5896. /* Return the value of NAME.  The return value is NULL if NAME is unset. */
  5897. static char *
  5898. set_p (char *name)
  5899. {
  5900.   register DEFINE *temp;
  5901.  
  5902.   for (temp = defines; temp; temp = temp->next)
  5903.     if (strcmp (temp->name, name) == 0)
  5904.       return (temp->value);
  5905.  
  5906.   return ((char *)NULL);
  5907. }
  5908.  
  5909. /* Conditionally parse based on the current command name. */
  5910. static void
  5911. command_name_condition (void)
  5912. {
  5913.   char *discarder;
  5914.  
  5915.   discarder = (char *)xmalloc (8 + strlen (command));
  5916.  
  5917.   sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
  5918.   discard_until (discarder);
  5919.   discard_until ("\n");
  5920.  
  5921.   free (discarder);
  5922. }
  5923.  
  5924. /* Create a variable whose name appears as the first word on this line. */
  5925. static void
  5926. cm_set (void)
  5927. {
  5928.   handle_variable (SET);
  5929. }
  5930.  
  5931. /* Remove a variable whose name appears as the first word on this line. */
  5932. static void
  5933. cm_clear (void)
  5934. {
  5935.   handle_variable (CLEAR);
  5936. }
  5937.  
  5938. static void
  5939. cm_ifset (void)
  5940. {
  5941.   handle_variable (IFSET);
  5942. }
  5943.  
  5944. static void
  5945. cm_ifclear (void)
  5946. {
  5947.   handle_variable (IFCLEAR);
  5948. }
  5949.  
  5950. /* This command takes braces, but we parse the contents specially, so we
  5951.    don't use the standard brace popping code.
  5952.  
  5953.    The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands
  5954.    if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
  5955.    it produces no output. */
  5956. static void
  5957. cm_ifeq (void)
  5958. {
  5959.   char **arglist;
  5960.  
  5961.   arglist = get_brace_args (0);
  5962.  
  5963.   if (arglist)
  5964.     {
  5965.       if (array_len (arglist) > 1)
  5966.     {
  5967.       if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
  5968.           (arglist[2] != (char *)NULL))
  5969.         execute_string ("%s\n", arglist[2]);
  5970.     }
  5971.  
  5972.       free_array (arglist);
  5973.     }
  5974. }
  5975.  
  5976. static void
  5977. cm_value (int arg, int start_pos, int end_pos)
  5978. {
  5979.   if (arg == END)
  5980.     {
  5981.       char *name, *value;
  5982.       name = (char *)&output_paragraph[start_pos];
  5983.       output_paragraph[end_pos] = '\0';
  5984.       name = strdup (name);
  5985.       value = set_p (name);
  5986.       output_column -= end_pos - start_pos;
  5987.       output_paragraph_offset = start_pos;
  5988.  
  5989.       if (value)
  5990.         execute_string ("%s", value);
  5991.       else
  5992.     add_word_args ("{No Value For \"%s\"}", name);
  5993.  
  5994.       free (name);
  5995.     }
  5996. }
  5997.  
  5998. /* Set, clear, or conditionalize based on ACTION. */
  5999. static void
  6000. handle_variable (int action)
  6001. {
  6002.   char *name;
  6003.  
  6004.   get_rest_of_line (&name);
  6005.   backup_input_pointer ();
  6006.   canon_white (name);
  6007.   handle_variable_internal (action, name);
  6008.   free (name);
  6009. }
  6010.  
  6011. static void
  6012. handle_variable_internal (int action, char *name)
  6013. {
  6014.   char *temp;
  6015.   int delimiter, additional_text_present = 0;
  6016.  
  6017.   /* Only the first word of NAME is a valid tag. */
  6018.   temp = name;
  6019.   delimiter = 0;
  6020.   while (*temp && (delimiter || !whitespace (*temp)))
  6021.     {
  6022. /* #if defined (SET_WITH_EQUAL) */
  6023.       if (*temp == '"' || *temp == '\'')
  6024.     {
  6025.       if (*temp == delimiter)
  6026.         delimiter = 0;
  6027.       else
  6028.         delimiter = *temp;
  6029.     }
  6030. /* #endif SET_WITH_EQUAL */
  6031.       temp++;
  6032.     }
  6033.  
  6034.   if (*temp)
  6035.     additional_text_present++;
  6036.  
  6037.   *temp = '\0';
  6038.  
  6039.   if (!*name)
  6040.     line_error ("%c%s requires a name", COMMAND_PREFIX, command);
  6041.   else
  6042.     {
  6043.       switch (action)
  6044.     {
  6045.     case SET:
  6046.       {
  6047.         char *value;
  6048.  
  6049. #if defined (SET_WITH_EQUAL)
  6050.         /* Allow a value to be saved along with a variable.  The value is
  6051.            the text following an `=' sign in NAME, if any is present. */
  6052.  
  6053.         for (value = name; *value && *value != '='; value++);
  6054.  
  6055.         if (*value)
  6056.           *value++ = '\0';
  6057.  
  6058.         if (*value == '"' || *value == '\'')
  6059.           {
  6060.         value++;
  6061.         value[strlen (value) - 1] = '\0';
  6062.           }
  6063.  
  6064. #else /* !SET_WITH_EQUAL */
  6065.         /* The VALUE of NAME is the remainder of the line sans
  6066.            whitespace. */
  6067.         if (additional_text_present)
  6068.           {
  6069.         value = temp + 1;
  6070.         canon_white (value);
  6071.           }
  6072.         else
  6073.           value = "";
  6074. #endif /* !SET_WITH_VALUE */
  6075.  
  6076.         set (name, value);
  6077.       }
  6078.       break;
  6079.  
  6080.     case CLEAR:
  6081.       clear (name);
  6082.       break;
  6083.  
  6084.     case IFSET:
  6085.     case IFCLEAR:
  6086.       /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
  6087.          read lines from the the file until we reach a matching
  6088.          "@end CONDITION".  This means that we only take note of
  6089.          "@ifset/clear" and "@end" commands. */
  6090.       {
  6091.         char condition[8];
  6092.         int condition_len;
  6093.  
  6094.         if (action == IFSET)
  6095.           strcpy (condition, "ifset");
  6096.         else
  6097.           strcpy (condition, "ifclear");
  6098.  
  6099.         condition_len = strlen (condition);
  6100.  
  6101.       if ((action == IFSET && !set_p (name)) ||
  6102.           (action == IFCLEAR && set_p (name)))
  6103.         {
  6104.           int level = 0, done = 0;
  6105.  
  6106.           while (!done)
  6107.         {
  6108.           char *freeable_line, *line;
  6109.  
  6110.           get_rest_of_line (&freeable_line);
  6111.  
  6112.           for (line = freeable_line; whitespace (*line); line++);
  6113.  
  6114.           if (*line == COMMAND_PREFIX &&
  6115.               (strncmp (line + 1, condition, condition_len) == 0))
  6116.             level++;
  6117.           else if (strncmp (line, "@end", 4) == 0)
  6118.             {
  6119.               char *cname = line + 4;
  6120.               char *temp;
  6121.  
  6122.               while (*cname && whitespace (*cname))
  6123.             cname++;
  6124.               temp = cname;
  6125.  
  6126.               while (*temp && !whitespace (*temp))
  6127.             temp++;
  6128.               *temp = '\0';
  6129.  
  6130.               if (strcmp (cname, condition) == 0)
  6131.             {
  6132.               if (!level)
  6133.                 {
  6134.                   done = 1;
  6135.                 }
  6136.               else
  6137.                 level--;
  6138.             }
  6139.             }
  6140.           free (freeable_line);
  6141.         }
  6142.           /* We found the end of a false @ifset/ifclear.  If we are
  6143.          in a menu, back up over the newline that ends the ifset,
  6144.          since that newline may also begin the next menu entry. */
  6145.           break;
  6146.         }
  6147.       else
  6148.         {
  6149.           if (action == IFSET)
  6150.         begin_insertion (ifset);
  6151.           else
  6152.         begin_insertion (ifclear);
  6153.         }
  6154.       }
  6155.       break;
  6156.     }
  6157.     }
  6158. }
  6159.  
  6160.  
  6161. /* **************************************************************** */
  6162. /*                                    */
  6163. /*            Execution of Random Text not in file        */
  6164. /*                                    */
  6165. /* **************************************************************** */
  6166.  
  6167. typedef struct {
  6168.   char *string;            /* The string buffer. */
  6169.   int size;            /* The size of the buffer. */
  6170.   int in_use;            /* Non-zero means string currently in use. */
  6171. } EXECUTION_STRING;
  6172.  
  6173. static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
  6174. static int execution_strings_index = 0;
  6175. static int execution_strings_slots = 0;
  6176.  
  6177. static EXECUTION_STRING *
  6178. get_execution_string (int initial_size)
  6179. {
  6180.   register int i = 0;
  6181.   EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
  6182.  
  6183.   if (execution_strings)
  6184.     {
  6185.       for (i = 0; i < execution_strings_index; i++)
  6186.     if (execution_strings[i] && (execution_strings[i]->in_use == 0))
  6187.       {
  6188.         es = execution_strings[i];
  6189.         break;
  6190.       }
  6191.     }
  6192.  
  6193.   if (!es)
  6194.     {
  6195.       if (execution_strings_index + 1 >= execution_strings_slots)
  6196.     {
  6197.       execution_strings = (EXECUTION_STRING **)xrealloc
  6198.         (execution_strings,
  6199.          (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
  6200.       for (; i < execution_strings_slots; i++)
  6201.         execution_strings[i] = (EXECUTION_STRING *)NULL;
  6202.     }
  6203.  
  6204.       execution_strings[execution_strings_index] =
  6205.     (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
  6206.       es = execution_strings[execution_strings_index];
  6207.       execution_strings_index++;
  6208.  
  6209.       es->size = 0;
  6210.       es->string = (char *)NULL;
  6211.       es->in_use = 0;
  6212.     }
  6213.  
  6214.   if (initial_size > es->size)
  6215.     {
  6216.       es->string = (char *) xrealloc (es->string, initial_size);
  6217.       es->size = initial_size;
  6218.     }
  6219.   return (es);
  6220. }
  6221.  
  6222. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  6223.    is like submitting a new file with @include. */
  6224.  
  6225. static void
  6226. execute_string (char *format, ...)
  6227. {
  6228.   va_list ap;
  6229.   EXECUTION_STRING *es;
  6230.   char *temp_string;
  6231.  
  6232.   es = get_execution_string (4000);
  6233.   temp_string = es->string;
  6234.   es->in_use = 1;
  6235.  
  6236.   va_start (ap, format);
  6237.   vsprintf (temp_string, format, ap);
  6238.   va_end (ap);
  6239.  
  6240.   pushfile ();
  6241.   input_text_offset = 0;
  6242.   input_text = temp_string;
  6243.   input_filename = strdup (input_filename);
  6244.   size_of_input_text = strlen (temp_string);
  6245.  
  6246.   executing_string++;
  6247.   reader_loop ();
  6248.   free (input_filename);
  6249.  
  6250.   popfile ();
  6251.   executing_string--;
  6252.   es->in_use = 0;
  6253. }
  6254.  
  6255. /* **************************************************************** */
  6256. /*                                    */
  6257. /*            @itemx, @item                    */
  6258. /*                                    */
  6259. /* **************************************************************** */
  6260.  
  6261. static int itemx_flag = 0;
  6262.  
  6263. static void
  6264. cm_itemx (void)
  6265. {
  6266.   itemx_flag++;
  6267.   cm_item ();
  6268.   itemx_flag--;
  6269. }
  6270.  
  6271. static void
  6272. cm_item (void)
  6273. {
  6274.   char *rest_of_line, *item_func;
  6275.  
  6276.   /* Can only hack "@item" while inside of an insertion. */
  6277.   if (insertion_level)
  6278.     {
  6279.       INSERTION_ELT *stack = insertion_stack;
  6280.       int original_input_text_offset;
  6281.  
  6282.       skip_whitespace ();
  6283.       original_input_text_offset = input_text_offset;
  6284.  
  6285.       get_rest_of_line (&rest_of_line);
  6286.       canon_white (rest_of_line);
  6287.       item_func = current_item_function ();
  6288.  
  6289.       /* Okay, do the right thing depending on which insertion function
  6290.      is active. */
  6291.  
  6292.     switch_top:
  6293.       switch (stack->insertion)
  6294.     {
  6295.     case ifinfo:
  6296.     case ifset:
  6297.     case ifclear:
  6298.     case cartouche:
  6299.       stack = stack->next;
  6300.       if (!stack)
  6301.         goto no_insertion;
  6302.       else
  6303.         goto switch_top;
  6304.       break;
  6305.  
  6306.     case menu:
  6307.     case quotation:
  6308.     case example:
  6309.     case smallexample:
  6310.     case lisp:
  6311.     case format:
  6312.     case display:
  6313.     case group:
  6314.       line_error ("The `%c%s' command is meaningless within a `@%s' block",
  6315.               COMMAND_PREFIX, command,
  6316.               insertion_type_pname (current_insertion_type ()));
  6317.       break;
  6318.  
  6319.     case itemize:
  6320.     case enumerate:
  6321.       if (itemx_flag)
  6322.         {
  6323.           line_error ("%citemx is not meaningful inside of a `%s' block",
  6324.               COMMAND_PREFIX,
  6325.               insertion_type_pname (current_insertion_type ()));
  6326.         }
  6327.       else
  6328.         {
  6329.           start_paragraph ();
  6330.           kill_self_indent (-1);
  6331.           filling_enabled = indented_fill = 1;
  6332.  
  6333.           if (current_insertion_type () == itemize)
  6334.         {
  6335.           indent (output_column = current_indent - 2);
  6336.  
  6337.           /* I need some way to determine whether this command
  6338.              takes braces or not.  I believe the user can type
  6339.              either "@bullet" or "@bullet{}".  Of course, they
  6340.              can also type "o" or "#" or whatever else they want. */
  6341.           if (item_func && *item_func)
  6342.             {
  6343.               if (*item_func == COMMAND_PREFIX)
  6344.             if (item_func[strlen (item_func) - 1] != '}')
  6345.               execute_string ("%s{}", item_func);
  6346.             else
  6347.               execute_string ("%s", item_func);
  6348.               else
  6349.             execute_string ("%s", item_func);
  6350.             }
  6351.           insert (' ');
  6352.           output_column++;
  6353.         }
  6354.           else
  6355.         enumerate_item ();
  6356.  
  6357.           /* Special hack.  This makes close paragraph ignore you until
  6358.          the start_paragraph () function has been called. */
  6359.           must_start_paragraph = 1;
  6360.  
  6361.           /* Ultra special hack.  It appears that some people incorrectly
  6362.          place text directly after the @item, instead of on a new line
  6363.          by itself.  This happens to work in TeX, so I make it work
  6364.          here. */
  6365.           if (*rest_of_line)
  6366.         {
  6367.           line_number--;
  6368.           input_text_offset = original_input_text_offset;
  6369.         }
  6370.         }
  6371.       break;
  6372.  
  6373.     case table:
  6374.     case ftable:
  6375.     case vtable:
  6376.       {
  6377.         /* Get rid of extra characters. */
  6378.         kill_self_indent (-1);
  6379.  
  6380.         /* close_paragraph () almost does what we want.  The problem
  6381.            is when paragraph_is_open, and last_char_was_newline, and
  6382.            the last newline has been turned into a space, because
  6383.            filling_enabled. I handle it here. */
  6384.         if (last_char_was_newline && filling_enabled && paragraph_is_open)
  6385.           insert ('\n');
  6386.         close_paragraph ();
  6387.  
  6388. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  6389.         /* Indent on a new line, but back up one indentation level. */
  6390.         {
  6391.           int t;
  6392.  
  6393.           t = inhibit_paragraph_indentation;
  6394.           inhibit_paragraph_indentation = 1;
  6395.           /* At this point, inserting any non-whitespace character will
  6396.          force the existing indentation to be output. */
  6397.           add_char ('i');
  6398.           inhibit_paragraph_indentation = t;
  6399.         }
  6400. #else /* !INDENT_PARAGRAPHS_IN_TABLE */
  6401.         add_char ('i');
  6402. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  6403.  
  6404.         output_paragraph_offset--;
  6405.         kill_self_indent (default_indentation_increment + 1);
  6406.  
  6407.         /* Add item's argument to the line. */
  6408.         filling_enabled = 0;
  6409.         if (item_func && *item_func)
  6410.            execute_string ("%s{%s}", item_func, rest_of_line);
  6411.          else
  6412.            execute_string ("%s", rest_of_line);
  6413.  
  6414.         if (current_insertion_type () == ftable)
  6415.           execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
  6416.  
  6417.         if (current_insertion_type () == vtable)
  6418.           execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
  6419.  
  6420.         /* Start a new line, and let start_paragraph ()
  6421.            do the indenting of it for you. */
  6422.         close_single_paragraph ();
  6423.         indented_fill = filling_enabled = 1;
  6424.       }
  6425.  
  6426.     default:
  6427.       break;
  6428.     }
  6429.       free (rest_of_line);
  6430.     }
  6431.   else
  6432.     {
  6433.     no_insertion:
  6434.       line_error ("%c%s found outside of an insertion block",
  6435.           COMMAND_PREFIX, command);
  6436.     }
  6437. }
  6438.  
  6439.  
  6440. /* **************************************************************** */
  6441. /*                                    */
  6442. /*            Defun and Friends                   */
  6443. /*                                    */
  6444. /* **************************************************************** */
  6445.  
  6446. #define DEFUN_SELF_DELIMITING(c)                    \
  6447.   (((c) == '(')                                \
  6448.    || ((c) == ')')                            \
  6449.    || ((c) == '[')                            \
  6450.    || ((c) == ']'))
  6451.  
  6452. struct token_accumulator
  6453. {
  6454.   unsigned int length;
  6455.   unsigned int index;
  6456.   char **tokens;
  6457. };
  6458.  
  6459. static void
  6460. initialize_token_accumulator (struct token_accumulator *accumulator)
  6461. {
  6462.   (accumulator->length) = 0;
  6463.   (accumulator->index) = 0;
  6464.   (accumulator->tokens) = NULL;
  6465. }
  6466.  
  6467. static void
  6468. accumulate_token (struct token_accumulator *accumulator, char *token)
  6469. {
  6470.   if ((accumulator->index) >= (accumulator->length))
  6471.     {
  6472.       (accumulator->length) += 10;
  6473.       (accumulator->tokens) = (char **) xrealloc
  6474.     (accumulator->tokens, (accumulator->length * sizeof (char *)));
  6475.     }
  6476.   accumulator->tokens[accumulator->index] = token;
  6477.   accumulator->index += 1;
  6478. }
  6479.  
  6480. static char *
  6481. copy_substring (char *start, char *end)
  6482. {
  6483.   char *result, *scan, *scan_result;
  6484.  
  6485.   result = (char *) xmalloc ((end - start) + 1);
  6486.   scan_result = result;
  6487.   scan = start;
  6488.  
  6489.   while (scan < end)
  6490.     *scan_result++ = *scan++;
  6491.  
  6492.   *scan_result = '\0';
  6493.   return (result);
  6494. }
  6495.  
  6496. /* Given `string' pointing at an open brace, skip forward and return a
  6497.    pointer to just past the matching close brace. */
  6498. static int
  6499. scan_group_in_string (char **string_pointer)
  6500. {
  6501.   register int c;
  6502.   register char *scan_string;
  6503.   register unsigned int level = 1;
  6504.  
  6505.   scan_string = (*string_pointer) + 1;
  6506.  
  6507.   while (1)
  6508.     {
  6509.       if (level == 0)
  6510.     {
  6511.       (*string_pointer) = scan_string;
  6512.       return (1);
  6513.     }
  6514.       c = (*scan_string++);
  6515.       if (c == '\0')
  6516.     {
  6517.       /* Tweak line_number to compensate for fact that
  6518.          we gobbled the whole line before coming here. */
  6519.       line_number -= 1;
  6520.       line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX);
  6521.       line_number += 1;
  6522.       (*string_pointer) = (scan_string - 1);
  6523.       return (0);
  6524.     }
  6525.       if (c == '{')
  6526.     level += 1;
  6527.       if (c == '}')
  6528.     level -= 1;
  6529.     }
  6530. }
  6531.  
  6532. /* Return a list of tokens from the contents of `string'.
  6533.    Commands and brace-delimited groups count as single tokens.
  6534.    Contiguous whitespace characters are converted to a token
  6535.    consisting of a single space. */
  6536. static char **
  6537. args_from_string (char *string)
  6538. {
  6539.   struct token_accumulator accumulator;
  6540.   register char *scan_string = string;
  6541.   char *token_start, *token_end;
  6542.  
  6543.   initialize_token_accumulator (&accumulator);
  6544.  
  6545.   while ((*scan_string) != '\0')
  6546.     {
  6547.       /* Replace arbitrary whitespace by a single space. */
  6548.       if (whitespace (*scan_string))
  6549.     {
  6550.       scan_string += 1;
  6551.       while (whitespace (*scan_string))
  6552.         scan_string += 1;
  6553.       accumulate_token ((&accumulator), (strdup (" ")));
  6554.       continue;
  6555.     }
  6556.  
  6557.       /* Commands count as single tokens. */
  6558.       if ((*scan_string) == COMMAND_PREFIX)
  6559.     {
  6560.       token_start = scan_string;
  6561.       scan_string += 1;
  6562.       if (self_delimiting (*scan_string))
  6563.         scan_string += 1;
  6564.       else
  6565.         {
  6566.           register int c;
  6567.           while (1)
  6568.         {
  6569.           c = *scan_string++;
  6570.  
  6571.            if ((c == '\0') || (c == '{') || (whitespace (c)))
  6572.             {
  6573.               scan_string -= 1;
  6574.               break;
  6575.             }
  6576.         }
  6577.  
  6578.           if (*scan_string == '{')
  6579.         {
  6580.           char *s = scan_string;
  6581.           (void) scan_group_in_string (&s);
  6582.           scan_string = s;
  6583.         }
  6584.         }
  6585.       token_end = scan_string;
  6586.     }
  6587.  
  6588.       /* Parentheses and brackets are self-delimiting. */
  6589.       else if (DEFUN_SELF_DELIMITING (*scan_string))
  6590.     {
  6591.       token_start = scan_string;
  6592.       scan_string += 1;
  6593.       token_end = scan_string;
  6594.     }
  6595.  
  6596.       /* Open brace introduces a group that is a single token. */
  6597.       else if (*scan_string == '{')
  6598.     {
  6599.       char *s = scan_string;
  6600.       int balanced = scan_group_in_string (&s);
  6601.  
  6602.       token_start = scan_string + 1;
  6603.       scan_string = s;
  6604.       token_end = balanced ? (scan_string - 1) : scan_string;
  6605.     }
  6606.  
  6607.       /* Otherwise a token is delimited by whitespace, parentheses,
  6608.      brackets, or braces.  A token is also ended by a command. */
  6609.       else
  6610.     {
  6611.       token_start = scan_string;
  6612.  
  6613.       while (1)
  6614.         {
  6615.           register int c;
  6616.  
  6617.           c = *scan_string++;
  6618.  
  6619.           if (!c ||
  6620.           (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
  6621.            c == '{' || c == '}'))
  6622.         {
  6623.           scan_string--;
  6624.           break;
  6625.         }
  6626.  
  6627.           /* If we encounter a command imbedded within a token,
  6628.          then end the token. */
  6629.           if (c == COMMAND_PREFIX)
  6630.         {
  6631.           scan_string--;
  6632.           break;
  6633.         }
  6634.         }
  6635.       token_end = scan_string;
  6636.     }
  6637.  
  6638.       accumulate_token
  6639.     (&accumulator, copy_substring (token_start, token_end));
  6640.     }
  6641.   accumulate_token (&accumulator, NULL);
  6642.   return (accumulator.tokens);
  6643. }
  6644.  
  6645. static void
  6646. process_defun_args (char **defun_args, int auto_var_p)
  6647. {
  6648.   int pending_space = 0;
  6649.  
  6650.   while (1)
  6651.     {
  6652.       char *defun_arg = *defun_args++;
  6653.  
  6654.       if (defun_arg == NULL)
  6655.     break;
  6656.  
  6657.       if (defun_arg[0] == ' ')
  6658.     {
  6659.       pending_space = 1;
  6660.       continue;
  6661.     }
  6662.  
  6663.       if (pending_space)
  6664.     {
  6665.       add_char (' ');
  6666.       pending_space = 0;
  6667.     }
  6668.  
  6669.       if (DEFUN_SELF_DELIMITING (defun_arg[0]))
  6670.     add_char (defun_arg[0]);
  6671.       else if (defun_arg[0] == '&')
  6672.     add_word (defun_arg);
  6673.       else if (defun_arg[0] == COMMAND_PREFIX)
  6674.     execute_string ("%s", defun_arg);
  6675.       else if (auto_var_p)
  6676.     execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
  6677.       else
  6678.     add_word (defun_arg);
  6679.     }
  6680. }
  6681.  
  6682. static char *
  6683. next_nonwhite_defun_arg (char ***arg_pointer)
  6684. {
  6685.   char **scan = (*arg_pointer);
  6686.   char *arg = (*scan++);
  6687.  
  6688.   if ((arg != 0) && (*arg == ' '))
  6689.     arg = *scan++;
  6690.  
  6691.   if (arg == 0)
  6692.     scan -= 1;
  6693.  
  6694.   *arg_pointer = scan;
  6695.  
  6696.   return ((arg == 0) ? "" : arg);
  6697. }
  6698.  
  6699. /* Make the defun type insertion.
  6700.    TYPE says which insertion this is.
  6701.    X_P says not to start a new insertion if non-zero. */
  6702. static void
  6703. defun_internal (enum insertion_type type, int x_p)
  6704. {
  6705.   enum insertion_type base_type;
  6706.   char **defun_args, **scan_args;
  6707.   char *category, *defined_name, *type_name, *type_name2;
  6708.  
  6709.   {
  6710.     char *line;
  6711.     get_rest_of_line (&line);
  6712.     defun_args = (args_from_string (line));
  6713.     free (line);
  6714.   }
  6715.  
  6716.   scan_args = defun_args;
  6717.  
  6718.   switch (type)
  6719.     {
  6720.     case defun:
  6721.       category = "Function";
  6722.       base_type = deffn;
  6723.       break;
  6724.     case defmac:
  6725.       category = "Macro";
  6726.       base_type = deffn;
  6727.       break;
  6728.     case defspec:
  6729.       category = "Special Form";
  6730.       base_type = deffn;
  6731.       break;
  6732.     case defvar:
  6733.       category = "Variable";
  6734.       base_type = defvr;
  6735.       break;
  6736.     case defopt:
  6737.       category = "User Option";
  6738.       base_type = defvr;
  6739.       break;
  6740.     case deftypefun:
  6741.       category = "Function";
  6742.       base_type = deftypefn;
  6743.       break;
  6744.     case deftypevar:
  6745.       category = "Variable";
  6746.       base_type = deftypevr;
  6747.       break;
  6748.     case defivar:
  6749.       category = "Instance Variable";
  6750.       base_type = defcv;
  6751.       break;
  6752.     case defmethod:
  6753.       category = "Method";
  6754.       base_type = defop;
  6755.       break;
  6756.     case deftypemethod:
  6757.       category = "Method";
  6758.       base_type = deftypemethod;
  6759.       break;
  6760.     default:
  6761.       category = next_nonwhite_defun_arg (&scan_args);
  6762.       base_type = type;
  6763.       break;
  6764.     }
  6765.  
  6766.   if ((base_type == deftypefn)
  6767.       || (base_type == deftypevr)
  6768.       || (base_type == defcv)
  6769.       || (base_type == defop)
  6770.       || (base_type == deftypemethod))
  6771.     type_name = next_nonwhite_defun_arg (&scan_args);
  6772.  
  6773.   if (base_type == deftypemethod)
  6774.     type_name2 = next_nonwhite_defun_arg (&scan_args);
  6775.  
  6776.   defined_name = next_nonwhite_defun_arg (&scan_args);
  6777.  
  6778.   /* This hack exists solely for the purposes of formatting the texinfo
  6779.      manual.  I couldn't think of a better way.  The token might be
  6780.      a simple @@ followed immediately by more text.  If this is the case,
  6781.      then the next defun arg is part of this one, and we should concatenate
  6782.      them. */
  6783.   if (*scan_args && **scan_args && !whitespace (**scan_args) &&
  6784.       (strcmp (defined_name, "@@") == 0))
  6785.     {
  6786.       char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
  6787.  
  6788.       sprintf (tem, "@@%s", scan_args[0]);
  6789.  
  6790.       free (scan_args[0]);
  6791.       scan_args[0] = tem;
  6792.       scan_args++;
  6793.       defined_name = tem;
  6794.     }
  6795.  
  6796.   if (!x_p)
  6797.     begin_insertion (type);
  6798.  
  6799.   /* Write the definition header line.
  6800.      This should start at the normal indentation.  */
  6801.   current_indent -= default_indentation_increment;
  6802.   start_paragraph ();
  6803.  
  6804.   switch (base_type)
  6805.     {
  6806.     case deffn:
  6807.     case defvr:
  6808.     case deftp:
  6809.       execute_string (" -- %s: %s", category, defined_name);
  6810.       break;
  6811.     case deftypefn:
  6812.     case deftypevr:
  6813.       execute_string (" -- %s: %s %s", category, type_name, defined_name);
  6814.       break;
  6815.     case defcv:
  6816.       execute_string (" -- %s of %s: %s", category, type_name, defined_name);
  6817.       break;
  6818.     case defop:
  6819.       execute_string (" -- %s on %s: %s", category, type_name, defined_name);
  6820.       break;
  6821.     case deftypemethod:
  6822.       execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
  6823.               defined_name);
  6824.       break;
  6825.     default:
  6826.       break;
  6827.     }
  6828.   current_indent += default_indentation_increment;
  6829.  
  6830.   /* Now process the function arguments, if any.
  6831.      If these carry onto the next line, they should be indented by two
  6832.      increments to distinguish them from the body of the definition,
  6833.      which is indented by one increment.  */
  6834.   current_indent += default_indentation_increment;
  6835.  
  6836.   switch (base_type)
  6837.     {
  6838.     case deffn:
  6839.     case defop:
  6840.       process_defun_args (scan_args, 1);
  6841.       break;
  6842.     case deftp:
  6843.     case deftypefn:
  6844.     case deftypemethod:
  6845.       process_defun_args (scan_args, 0);
  6846.       break;
  6847.     default:
  6848.       break;
  6849.     }
  6850.   current_indent -= default_indentation_increment;
  6851.   close_single_paragraph ();
  6852.  
  6853.   /* Make an entry in the appropriate index. */
  6854.   switch (base_type)
  6855.     {
  6856.     case deffn:
  6857.     case deftypefn:
  6858.       execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
  6859.       break;
  6860.     case defvr:
  6861.     case deftypevr:
  6862.     case defcv:
  6863.       execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
  6864.       break;
  6865.     case defop:
  6866.     case deftypemethod:
  6867.       execute_string ("%cfindex %s on %s\n",
  6868.               COMMAND_PREFIX, defined_name, type_name);
  6869.       break;
  6870.     case deftp:
  6871.       execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
  6872.       break;
  6873.     default:
  6874.       break;
  6875.     }
  6876.  
  6877.   /* Deallocate the token list. */
  6878.   scan_args = defun_args;
  6879.   while (1)
  6880.     {
  6881.       char * arg = (*scan_args++);
  6882.       if (arg == NULL)
  6883.     break;
  6884.       free (arg);
  6885.     }
  6886.   free (defun_args);
  6887. }
  6888.  
  6889. /* Add an entry for a function, macro, special form, variable, or option.
  6890.    If the name of the calling command ends in `x', then this is an extra
  6891.    entry included in the body of an insertion of the same type. */
  6892. static void
  6893. cm_defun (void)
  6894. {
  6895.   int x_p;
  6896.   enum insertion_type type;
  6897.   char *temp = strdup (command);
  6898.  
  6899.   x_p = (command[strlen (command) - 1] == 'x');
  6900.  
  6901.   if (x_p)
  6902.     temp[strlen (temp) - 1] = '\0';
  6903.  
  6904.   type = find_type_from_name (temp);
  6905.   free (temp);
  6906.  
  6907.   /* If we are adding to an already existing insertion, then make sure
  6908.      that we are already in an insertion of type TYPE. */
  6909.   if (x_p &&
  6910.       (!insertion_level || insertion_stack->insertion != type))
  6911.     {
  6912.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  6913.           command, command);
  6914.       discard_until ("\n");
  6915.       return;
  6916.     }
  6917.  
  6918.   defun_internal (type, x_p);
  6919. }
  6920.  
  6921. /* End existing insertion block. */
  6922. static void
  6923. cm_end (void)
  6924. {
  6925.   char *temp;
  6926.   enum insertion_type type;
  6927.  
  6928.   get_rest_of_line (&temp);
  6929.   canon_white (temp);
  6930.  
  6931.   if (strlen (temp) == 0)
  6932.     line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command);
  6933.  
  6934.   type = find_type_from_name (temp);
  6935.  
  6936.   if (type == bad_type)
  6937.     {
  6938.       line_error ("Bad argument to `%s', `%s', using `%s'",
  6939.        command, temp, insertion_type_pname (current_insertion_type ()));
  6940.     }
  6941.  
  6942.   /* see comment under cm_ifinfo() */
  6943.   if (type == ifinfo)
  6944.     {
  6945.       if (!ifinfo_count)
  6946.     line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
  6947.       else
  6948.     ifinfo_count--;
  6949.     }
  6950.   else
  6951.     {
  6952.       if (!insertion_level)
  6953.     line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
  6954.       else
  6955.     end_insertion (type);
  6956.     }
  6957.  
  6958.   free (temp);
  6959. }
  6960.  
  6961.  
  6962. /* **************************************************************** */
  6963. /*                                    */
  6964. /*            Other Random Commands                   */
  6965. /*                                    */
  6966. /* **************************************************************** */
  6967.  
  6968. /* This says to inhibit the indentation of the next paragraph, but
  6969.    not of following paragraphs.  */
  6970. static void
  6971. cm_noindent (void)
  6972. {
  6973.   if (!inhibit_paragraph_indentation)
  6974.     inhibit_paragraph_indentation = -1;
  6975. }
  6976.  
  6977. /* I don't know exactly what to do with this.  Should I allow
  6978.    someone to switch filenames in the middle of output?  Since the
  6979.    file could be partially written, this doesn't seem to make sense.
  6980.    Another option: ignore it, since they don't *really* want to
  6981.    switch files.  Finally, complain, or at least warn. */
  6982. static void
  6983. cm_setfilename (void)
  6984. {
  6985.   char *filename;
  6986.   get_rest_of_line (&filename);
  6987.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  6988.   free (filename);
  6989. }
  6990.  
  6991. static void
  6992. cm_ignore_line (void)
  6993. {
  6994.   discard_until ("\n");
  6995. }
  6996.  
  6997. /* @br can be immediately followed by `{}', so we have to read those here.
  6998.    It should simply close the paragraph. */
  6999. static void
  7000. cm_br (void)
  7001. {
  7002.   if (looking_at ("{}"))
  7003.     input_text_offset += 2;
  7004.  
  7005.   if (curchar () == '\n')
  7006.     {
  7007.       input_text_offset++;
  7008.       line_number++;
  7009.     }
  7010.  
  7011.   close_paragraph ();
  7012. }
  7013.  
  7014.  /* Insert the number of blank lines passed as argument. */
  7015. static void
  7016. cm_sp (void)
  7017. {
  7018.   int lines;
  7019.   char *line;
  7020.  
  7021.   get_rest_of_line (&line);
  7022.  
  7023.   if (sscanf (line, "%d", &lines) != 1)
  7024.     {
  7025.       line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
  7026.     }
  7027.   else
  7028.     {
  7029.       if (lines < 0)
  7030.     lines = 0;
  7031.  
  7032.       while (lines--)
  7033.     add_char ('\n');
  7034.     }
  7035.   free (line);
  7036. }
  7037.  
  7038. /* Start a new line with just this text on it.
  7039.    Then center the line of text.
  7040.    This always ends the current paragraph. */
  7041. static void
  7042. cm_center (void)
  7043. {
  7044.   register int i, start, length;
  7045.   int fudge_factor = 1;
  7046.   unsigned char *line;
  7047.  
  7048.   close_paragraph ();
  7049.   filling_enabled = indented_fill = 0;
  7050.   cm_noindent ();
  7051.   start = output_paragraph_offset;
  7052.   inhibit_output_flushing ();
  7053.   get_rest_of_line ((char **)&line);
  7054.   execute_string ((char *)line);
  7055.   free (line);
  7056.   uninhibit_output_flushing ();
  7057.  
  7058.   i = output_paragraph_offset - 1;
  7059.   while (i > (start - 1) && output_paragraph[i] == '\n')
  7060.     i--;
  7061.  
  7062.   output_paragraph_offset = ++i;
  7063.   length = output_paragraph_offset - start;
  7064.  
  7065.   if (length < (fill_column - fudge_factor))
  7066.     {
  7067.       line = (unsigned char *)xmalloc (1 + length);
  7068.       memcpy (line, (char *)(output_paragraph + start), length);
  7069.  
  7070.       i = (fill_column - fudge_factor - length) / 2;
  7071.       output_paragraph_offset = start;
  7072.  
  7073.       while (i--)
  7074.     insert (' ');
  7075.  
  7076.       for (i = 0; i < length; i++)
  7077.     insert (line[i]);
  7078.  
  7079.       free (line);
  7080.     }
  7081.  
  7082.   insert ('\n');
  7083.   close_paragraph ();
  7084.   filling_enabled = 1;
  7085. }
  7086.  
  7087. /* Show what an expression returns. */
  7088. static void
  7089. cm_result (int arg)
  7090. {
  7091.   if (arg == END)
  7092.     add_word ("=>");
  7093. }
  7094.  
  7095. /* What an expression expands to. */
  7096. static void
  7097. cm_expansion (int arg)
  7098. {
  7099.   if (arg == END)
  7100.     add_word ("==>");
  7101. }
  7102.  
  7103. /* Indicates two expressions are equivalent. */
  7104. static void
  7105. cm_equiv (int arg)
  7106. {
  7107.   if (arg == END)
  7108.     add_word ("==");
  7109. }
  7110.  
  7111. /* What an expression may print. */
  7112. static void
  7113. cm_print (int arg)
  7114. {
  7115.   if (arg == END)
  7116.     add_word ("-|");
  7117. }
  7118.  
  7119. /* An error signaled. */
  7120. static void
  7121. cm_error (int arg)
  7122. {
  7123.   if (arg == END)
  7124.     add_word ("error-->");
  7125. }
  7126.  
  7127. /* The location of point in an example of a buffer. */
  7128. static void
  7129. cm_point (int arg)
  7130. {
  7131.   if (arg == END)
  7132.     add_word ("-!-");
  7133. }
  7134.  
  7135. /* Start a new line with just this text on it.
  7136.    The text is outdented one level if possible. */
  7137. static void
  7138. cm_exdent (void)
  7139. {
  7140.   char *line;
  7141.   int i = current_indent;
  7142.  
  7143.   if (current_indent)
  7144.     current_indent -= default_indentation_increment;
  7145.  
  7146.   get_rest_of_line (&line);
  7147.   close_single_paragraph ();
  7148.   execute_string ("%s", line);
  7149.   current_indent = i;
  7150.   free (line);
  7151.   close_single_paragraph ();
  7152. }
  7153.  
  7154. static void
  7155. cm_include (void)
  7156. {
  7157.   cm_infoinclude ();
  7158. }
  7159.  
  7160. #if !defined (HAVE_STRERROR)
  7161. extern char *sys_errlist[];
  7162. extern int sys_nerr;
  7163.  
  7164. char *
  7165. strerror (num)
  7166.      int num;
  7167. {
  7168.   if (num >= sys_nerr)
  7169.     return ("Unknown file system error");
  7170.   else
  7171.     return (sys_errlist[num]);
  7172. }
  7173. #endif /* !HAVE_STRERROR */
  7174.  
  7175. /* Remember this file, and move onto the next. */
  7176. static void
  7177. cm_infoinclude (void)
  7178. {
  7179.   char *filename;
  7180.  
  7181. #if defined (HAVE_MACROS)
  7182.   if (macro_expansion_output_stream)
  7183.     me_append_before_this_command ();
  7184. #endif /* HAVE_MACROS */
  7185.  
  7186.   close_paragraph ();
  7187.   get_rest_of_line (&filename);
  7188.  
  7189. #if defined (HAVE_MACROS)
  7190.   if (macro_expansion_output_stream)
  7191.     remember_itext (input_text, input_text_offset);
  7192. #endif /* HAVE_MACROS */
  7193.  
  7194.   pushfile ();
  7195.  
  7196.   /* In verbose mode we print info about including another file. */
  7197.   if (verbose_mode)
  7198.     {
  7199.       register int i = 0;
  7200.       register FSTACK *stack = filestack;
  7201.  
  7202.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  7203.  
  7204.       i *= 2;
  7205.  
  7206.       printf ("%*s", i, "");
  7207.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  7208.       fflush (stdout);
  7209.     }
  7210.  
  7211.   if (!find_and_load (filename))
  7212.     {
  7213.       popfile ();
  7214.       line_number--;
  7215.  
  7216.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  7217.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  7218.           strerror (errno));
  7219.  
  7220.       free (filename);
  7221.       return;
  7222.     }
  7223.   else
  7224.     {
  7225. #if defined (HAVE_MACROS)
  7226.       if (macro_expansion_output_stream)
  7227.     remember_itext (input_text, input_text_offset);
  7228. #endif /* HAVE_MACROS */
  7229.       reader_loop ();
  7230.     }
  7231.   free (filename);
  7232.   popfile ();
  7233. }
  7234.  
  7235. /* The other side of a malformed expression. */
  7236. static void
  7237. misplaced_brace (void)
  7238. {
  7239.   line_error ("Misplaced `}'");
  7240. }
  7241.  
  7242. /* Don't let the filling algorithm insert extra whitespace here. */
  7243. static void
  7244. cm_force_abbreviated_whitespace (void)
  7245. {
  7246. }
  7247.  
  7248. /* Do not let this character signify the end of a sentence, though
  7249.    if it was seen without the command prefix it normally would.  We
  7250.    do this by turning on the 8th bit of the character. */
  7251. static void
  7252. cm_ignore_sentence_ender (void)
  7253. {
  7254.   add_char (META ((*command)));
  7255. }
  7256.  
  7257. /* Signals end of processing.  Easy to make this happen. */
  7258. static void
  7259. cm_bye (void)
  7260. {
  7261.   input_text_offset = size_of_input_text;
  7262. }
  7263.  
  7264. static void
  7265. cm_asis (void)
  7266. {
  7267. }
  7268.  
  7269. static void
  7270. cm_math (void)
  7271. {
  7272. }
  7273.  
  7274.  
  7275. /* **************************************************************** */
  7276. /*                                    */
  7277. /*            Indexing Stuff                    */
  7278. /*                                    */
  7279. /* **************************************************************** */
  7280.  
  7281.  
  7282. /* An index element... */
  7283. typedef struct index_elt
  7284. {
  7285.   struct index_elt *next;
  7286.   char *entry;            /* The index entry itself. */
  7287.   char *node;            /* The node from whence it came. */
  7288.   int code;            /* Non-zero means add `@code{...}' when
  7289.                    printing this element. */
  7290.   int defining_line;        /* Line number where this entry was written. */
  7291. } INDEX_ELT;
  7292.  
  7293. /* A list of short-names for each index, and the index to that index in our
  7294.    index array, the_indices.  In addition, for each index, it is remembered
  7295.    whether that index is a code index or not.  Code indices have @code{}
  7296.    inserted around the first word when they are printed with printindex. */
  7297. typedef struct
  7298. {
  7299.   char *name;
  7300.   int index;
  7301.   int code;
  7302. } INDEX_ALIST;
  7303.  
  7304. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  7305.  
  7306. /* An array of pointers.  Each one is for a different index.  The
  7307.    "synindex" command changes which array slot is pointed to by a
  7308.    given "index". */
  7309. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  7310.  
  7311. /* The number of defined indices. */
  7312. int defined_indices = 0;
  7313.  
  7314. /* We predefine these. */
  7315. #define program_index 0
  7316. #define function_index 1
  7317. #define concept_index 2
  7318. #define variable_index 3
  7319. #define datatype_index 4
  7320. #define key_index 5
  7321.  
  7322. static void
  7323. init_indices (void)
  7324. {
  7325.   int i;
  7326.  
  7327.   /* Create the default data structures. */
  7328.  
  7329.   /* Initialize data space. */
  7330.   if (!the_indices)
  7331.     {
  7332.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  7333.                         sizeof (INDEX_ELT *));
  7334.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  7335.  
  7336.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  7337.                            sizeof (INDEX_ALIST *));
  7338.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  7339.     }
  7340.  
  7341.   /* If there were existing indices, get rid of them now. */
  7342.   for (i = 0; i < defined_indices; i++)
  7343.     undefindex (name_index_alist[i]->name);
  7344.  
  7345.   /* Add the default indices. */
  7346.   top_defindex ("pg", 0);
  7347.   top_defindex ("fn", 1);        /* "fn" is a code index.  */
  7348.   top_defindex ("cp", 0);
  7349.   top_defindex ("vr", 0);
  7350.   top_defindex ("tp", 0);
  7351.   top_defindex ("ky", 0);
  7352.  
  7353. }
  7354.  
  7355. /* Find which element in the known list of indices has this name.
  7356.    Returns -1 if NAME isn't found. */
  7357. static int
  7358. find_index_offset (char *name)
  7359. {
  7360.   register int i;
  7361.   for (i = 0; i < defined_indices; i++)
  7362.     if (name_index_alist[i] &&
  7363.     strcmp (name, name_index_alist[i]->name) == 0)
  7364.       return (name_index_alist[i]->index);
  7365.   return (-1);
  7366. }
  7367.  
  7368. /* Return a pointer to the entry of (name . index) for this name.
  7369.    Return NULL if the index doesn't exist. */
  7370. static INDEX_ALIST *
  7371. find_index (char *name)
  7372. {
  7373.   int offset = find_index_offset (name);
  7374.   if (offset > -1)
  7375.     return (name_index_alist[offset]);
  7376.   else
  7377.     return ((INDEX_ALIST *) NULL);
  7378. }
  7379.  
  7380. /* Given an index name, return the offset in the_indices of this index,
  7381.    or -1 if there is no such index. */
  7382. static int
  7383. translate_index (char *name)
  7384. {
  7385.   INDEX_ALIST *which = find_index (name);
  7386.  
  7387.   if (which)
  7388.     return (which->index);
  7389.   else
  7390.     return (-1);
  7391. }
  7392.  
  7393. /* Return the index list which belongs to NAME. */
  7394. static INDEX_ELT *
  7395. index_list (char *name)
  7396. {
  7397.   int which = translate_index (name);
  7398.   if (which < 0)
  7399.     return ((INDEX_ELT *) -1);
  7400.   else
  7401.     return (the_indices[which]);
  7402. }
  7403.  
  7404. /* Please release me, let me go... */
  7405. static void
  7406. free_index (INDEX_ELT *index)
  7407. {
  7408.   INDEX_ELT *temp;
  7409.  
  7410.   while ((temp = index) != (INDEX_ELT *) NULL)
  7411.     {
  7412.       free (temp->entry);
  7413.       free (temp->node);
  7414.       index = index->next;
  7415.       free (temp);
  7416.     }
  7417. }
  7418.  
  7419. /* Flush an index by name. */
  7420. static void
  7421. undefindex (char *name)
  7422. {
  7423.   int i;
  7424.   int which = find_index_offset (name);
  7425.  
  7426.   if (which < 0)
  7427.     return;
  7428.  
  7429.   i = name_index_alist[which]->index;
  7430.  
  7431.   free_index (the_indices[i]);
  7432.   the_indices[i] = (INDEX_ELT *) NULL;
  7433.  
  7434.   free (name_index_alist[which]->name);
  7435.   free (name_index_alist[which]);
  7436.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  7437. }
  7438.  
  7439. /* Define an index known as NAME.  We assign the slot number.
  7440.    CODE if non-zero says to make this a code index. */
  7441. static void
  7442. defindex (char *name, int code)
  7443. {
  7444.   register int i, slot;
  7445.  
  7446.   /* If it already exists, flush it. */
  7447.   undefindex (name);
  7448.  
  7449.   /* Try to find an empty slot. */
  7450.   slot = -1;
  7451.   for (i = 0; i < defined_indices; i++)
  7452.     if (!name_index_alist[i])
  7453.       {
  7454.     slot = i;
  7455.     break;
  7456.       }
  7457.  
  7458.   if (slot < 0)
  7459.     {
  7460.       /* No such luck.  Make space for another index. */
  7461.       slot = defined_indices;
  7462.       defined_indices++;
  7463.  
  7464.       name_index_alist = (INDEX_ALIST **)
  7465.     xrealloc ((char *)name_index_alist,
  7466.           (1 + defined_indices) * sizeof (INDEX_ALIST *));
  7467.       the_indices = (INDEX_ELT **)
  7468.     xrealloc ((char *)the_indices,
  7469.           (1 + defined_indices) * sizeof (INDEX_ELT *));
  7470.     }
  7471.  
  7472.   /* We have a slot.  Start assigning. */
  7473.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  7474.   name_index_alist[slot]->name = strdup (name);
  7475.   name_index_alist[slot]->index = slot;
  7476.   name_index_alist[slot]->code = code;
  7477.  
  7478.   the_indices[slot] = (INDEX_ELT *) NULL;
  7479. }
  7480.  
  7481. /* Add the arguments to the current index command to the index NAME. */
  7482. static void
  7483. index_add_arg (char *name)
  7484. {
  7485.   int which;
  7486.   char *index_entry;
  7487.   INDEX_ALIST *tem;
  7488.  
  7489.   tem = find_index (name);
  7490.  
  7491.   which = tem ? tem->index : -1;
  7492.  
  7493. #if defined (HAVE_MACROS)
  7494.   if (macro_expansion_output_stream)
  7495.     append_to_expansion_output (input_text_offset + 1);
  7496. #endif /* HAVE_MACROS */
  7497.  
  7498.   get_rest_of_line (&index_entry);
  7499.   ignore_blank_line ();
  7500.  
  7501. #if defined (HAVE_MACROS)
  7502.   if (macro_expansion_output_stream)
  7503.     {
  7504.       int op_orig;
  7505.  
  7506.       remember_itext (input_text, input_text_offset);
  7507.       op_orig = output_paragraph_offset;
  7508.       me_execute_string (index_entry);
  7509.       me_execute_string ("\n");
  7510.       output_paragraph_offset = op_orig;
  7511.     }
  7512. #endif /* HAVE_MACROS */
  7513.  
  7514.   if (which < 0)
  7515.     {
  7516.       line_error ("Unknown index reference `%s'", name);
  7517.       free (index_entry);
  7518.     }
  7519.   else
  7520.     {
  7521.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  7522.       new->next = the_indices[which];
  7523.       new->entry = index_entry;
  7524.       new->node = current_node;
  7525.       new->code = tem->code;
  7526.       new->defining_line = line_number - 1;
  7527.       the_indices[which] = new;
  7528.     }
  7529. }
  7530.  
  7531. #define INDEX_COMMAND_SUFFIX "index"
  7532.  
  7533. /* The function which user defined index commands call. */
  7534. static void
  7535. gen_index (void)
  7536. {
  7537.   char *name = strdup (command);
  7538.   if ((int) strlen (name) >= (int) strlen ("index"))
  7539.     name[strlen (name) - strlen ("index")] = '\0';
  7540.   index_add_arg (name);
  7541.   free (name);
  7542. }
  7543.  
  7544. static void
  7545. top_defindex (char *name, int code)
  7546. {
  7547.   char *temp;
  7548.  
  7549.   temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
  7550.   sprintf (temp, "%sindex", name);
  7551.   define_user_command (temp, gen_index, 0);
  7552.   defindex (name, code);
  7553.   free (temp);
  7554. }
  7555.  
  7556. /* Define a new index command.  Arg is name of index. */
  7557. static void
  7558. cm_defindex (void)
  7559. {
  7560.   gen_defindex (0);
  7561. }
  7562.  
  7563. static void
  7564. cm_defcodeindex (void)
  7565. {
  7566.   gen_defindex (1);
  7567. }
  7568.  
  7569. static void
  7570. gen_defindex (int code)
  7571. {
  7572.   char *name;
  7573.   get_rest_of_line (&name);
  7574.  
  7575.   if (find_index (name))
  7576.     {
  7577.       line_error ("Index `%s' already exists", name);
  7578.       free (name);
  7579.       return;
  7580.     }
  7581.   else
  7582.     {
  7583.       char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
  7584.       sprintf (temp, "%sindex", name);
  7585.       define_user_command (temp, gen_index, 0);
  7586.       defindex (name, code);
  7587.       free (name);
  7588.     }
  7589. }
  7590.  
  7591. /* Append LIST2 to LIST1.  Return the head of the list. */
  7592. static INDEX_ELT *
  7593. index_append (INDEX_ELT *head, INDEX_ELT *tail)
  7594. {
  7595.   register INDEX_ELT *t_head = head;
  7596.  
  7597.   if (!t_head)
  7598.     return (tail);
  7599.  
  7600.   while (t_head->next)
  7601.     t_head = t_head->next;
  7602.   t_head->next = tail;
  7603.   return (head);
  7604. }
  7605.  
  7606. /* Expects 2 args, on the same line.  Both are index abbreviations.
  7607.    Make the first one be a synonym for the second one, i.e. make the
  7608.    first one have the same index as the second one. */
  7609. static void
  7610. cm_synindex (void)
  7611. {
  7612.   int redirector, redirectee;
  7613.   char *temp;
  7614.  
  7615.   skip_whitespace ();
  7616.   get_until_in_line (" ", &temp);
  7617.   redirectee = find_index_offset (temp);
  7618.   skip_whitespace ();
  7619.   free_and_clear (&temp);
  7620.   get_until_in_line (" ", &temp);
  7621.   redirector = find_index_offset (temp);
  7622.   free (temp);
  7623.   if (redirector < 0 || redirectee < 0)
  7624.     {
  7625.       line_error ("Unknown index reference");
  7626.     }
  7627.   else
  7628.     {
  7629.       /* I think that we should let the user make indices synonymous to
  7630.          each other without any lossage of info.  This means that one can
  7631.          say @synindex cp dt anywhere in the file, and things that used to
  7632.          be in cp will go into dt. */
  7633.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  7634.  
  7635.       if (i1 || i2)
  7636.     {
  7637.       if (i1)
  7638.         the_indices[redirectee] = index_append (i1, i2);
  7639.       else
  7640.         the_indices[redirectee] = index_append (i2, i1);
  7641.     }
  7642.  
  7643.       name_index_alist[redirectee]->index =
  7644.     name_index_alist[redirector]->index;
  7645.     }
  7646. }
  7647.  
  7648. static void
  7649. cm_pindex (void)            /* Pinhead index. */
  7650. {
  7651.   index_add_arg ("pg");
  7652. }
  7653.  
  7654. static void
  7655. cm_vindex (void)            /* Variable index. */
  7656. {
  7657.   index_add_arg ("vr");
  7658. }
  7659.  
  7660. static void
  7661. cm_kindex (void)            /* Key index. */
  7662. {
  7663.   index_add_arg ("ky");
  7664. }
  7665.  
  7666. static void
  7667. cm_cindex (void)            /* Concept index. */
  7668. {
  7669.   index_add_arg ("cp");
  7670. }
  7671.  
  7672. static void
  7673. cm_findex (void)            /* Function index. */
  7674. {
  7675.   index_add_arg ("fn");
  7676. }
  7677.  
  7678. static void
  7679. cm_tindex (void)            /* Data Type index. */
  7680. {
  7681.   index_add_arg ("tp");
  7682. }
  7683.  
  7684. /* Sorting the index. */
  7685. static int
  7686. index_element_compare (INDEX_ELT **element1, INDEX_ELT **element2)
  7687. {
  7688.   /* This needs to ignore leading non-text characters. */
  7689.   return (strcasecmp ((*element1)->entry, (*element2)->entry));
  7690. }
  7691.  
  7692. /* Force all index entries to be unique. */
  7693. static void
  7694. make_index_entries_unique (INDEX_ELT **array, int count)
  7695. {
  7696.   register int i, j;
  7697.   INDEX_ELT **copy;
  7698.  
  7699.   copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
  7700.  
  7701.   for (i = 0, j = 0; i < count; i++)
  7702.     {
  7703.       if ((i == (count - 1)) ||
  7704.       (array[i]->node != array[i + 1]->node) ||
  7705.       (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0))
  7706.     copy[j++] = array[i];
  7707.       else
  7708.     {
  7709.       free (array[i]->entry);
  7710.       free (array[i]);
  7711.     }
  7712.     }
  7713.   copy[j] = (INDEX_ELT *)NULL;
  7714.  
  7715.   /* Now COPY contains only unique entries.  Duplicated entries in the
  7716.      original array have been freed.  Replace the current array with
  7717.      the copy, fixing the NEXT pointers. */
  7718.   for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
  7719.     {
  7720.       int counter = 1;
  7721.       copy[i]->next = copy[i + 1];
  7722.  
  7723.       /* Fix entry names which are the same.  They point to different nodes,
  7724.      so we make the entry name unique. */
  7725.       if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
  7726.       (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
  7727.     {
  7728.       char *new_entry_name;
  7729.  
  7730.       new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
  7731.       sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
  7732.       free (copy[i]->entry);
  7733.       copy[i]->entry = new_entry_name;
  7734.       counter++;
  7735.     }
  7736.       else
  7737.     counter = 1;
  7738.  
  7739.       array[i] = copy[i];
  7740.     }
  7741.   array[i] = (INDEX_ELT *)NULL;
  7742.  
  7743.   /* Free the storage used only by COPY. */
  7744.   free (copy);
  7745. }
  7746.  
  7747. /* Sort the index passed in INDEX, returning an array of
  7748.    pointers to elements.  The array is terminated with a NULL
  7749.    pointer.  We call qsort because it's supposed to be fast.
  7750.    I think this looks bad. */
  7751. static INDEX_ELT **
  7752. sort_index (INDEX_ELT *index)
  7753. {
  7754.   INDEX_ELT *temp = index;
  7755.   INDEX_ELT **array;
  7756.   int count = 0;
  7757.  
  7758.   while (temp != (INDEX_ELT *) NULL)
  7759.     {
  7760.       count++;
  7761.       temp = temp->next;
  7762.     }
  7763.  
  7764.   /* We have the length.  Make an array. */
  7765.  
  7766.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  7767.   count = 0;
  7768.   temp = index;
  7769.  
  7770.   while (temp != (INDEX_ELT *) NULL)
  7771.     {
  7772.       array[count++] = temp;
  7773.       temp = temp->next;
  7774.     }
  7775.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  7776.  
  7777.   /* Sort the array. */
  7778.   qsort (array, count, sizeof (INDEX_ELT *), (int (*)()) index_element_compare);
  7779.   make_index_entries_unique (array, count);
  7780.   return (array);
  7781. }
  7782.  
  7783. /* Non-zero means that we are in the middle of printing an index. */
  7784. int printing_index = 0;
  7785.  
  7786. /* Takes one arg, a short name of an index to print.
  7787.    Outputs a menu of the sorted elements of the index. */
  7788. static void
  7789. cm_printindex (void)
  7790. {
  7791.   int item;
  7792.   INDEX_ELT *index;
  7793.   INDEX_ELT **array;
  7794.   char *index_name;
  7795.   int old_inhibitions = inhibit_paragraph_indentation;
  7796.   int previous_filling_enabled_value = filling_enabled;
  7797.  
  7798.   close_paragraph ();
  7799.   get_rest_of_line (&index_name);
  7800.  
  7801.   index = index_list (index_name);
  7802.   if (index == (INDEX_ELT *)-1)
  7803.     {
  7804.       line_error ("Unknown index name `%s'", index_name);
  7805.       free (index_name);
  7806.       return;
  7807.     }
  7808.   else
  7809.     free (index_name);
  7810.  
  7811.   array = sort_index (index);
  7812.  
  7813.   filling_enabled = 0;
  7814.   inhibit_paragraph_indentation = 1;
  7815.   close_paragraph ();
  7816.   add_word ("* Menu:\n\n");
  7817.  
  7818.   printing_index = 1;
  7819.  
  7820. #if defined (HAVE_MACROS)
  7821.   me_inhibit_expansion++;
  7822. #endif /* HAVE_MACROS */
  7823.  
  7824.   for (item = 0; (index = array[item]); item++)
  7825.     {
  7826.       int real_line_number = line_number;
  7827.  
  7828.       /* Let errors generated while making the index entry point back
  7829.      at the line which contains the entry. */
  7830.       line_number = index->defining_line;
  7831.  
  7832.       /* If this particular entry should be printed as a "code" index,
  7833.      then wrap the entry with "@code{...}". */
  7834.       if (index->code)
  7835.     execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, index->entry);
  7836.       else
  7837.     execute_string ("* %s: ", index->entry);
  7838.  
  7839.       /* Pad the front of the destination nodename so that
  7840.      the output looks nice. */
  7841.       if (fill_column > 40 && output_column < 40)
  7842.     indent (40 - output_column);
  7843.  
  7844.       execute_string ("%s.\n", index->node);
  7845.  
  7846.       line_number = real_line_number;
  7847.       flush_output ();
  7848.     }
  7849.  
  7850. #if defined (HAVE_MACROS)
  7851.       me_inhibit_expansion--;
  7852. #endif /* HAVE_MACROS */
  7853.  
  7854.   printing_index = 0;
  7855.   free (array);
  7856.   close_single_paragraph ();
  7857.   filling_enabled = previous_filling_enabled_value;
  7858.   inhibit_paragraph_indentation = old_inhibitions;
  7859. }
  7860.  
  7861.  
  7862. /* **************************************************************** */
  7863. /*                                    */
  7864. /*            Making User Defined Commands            */
  7865. /*                                    */
  7866. /* **************************************************************** */
  7867.  
  7868. static void
  7869. define_user_command (char *name, COMMAND_FUNCTION *proc, int needs_braces_p)
  7870. {
  7871.   int slot = user_command_array_len;
  7872.   user_command_array_len++;
  7873.  
  7874.   if (!user_command_array)
  7875.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  7876.  
  7877.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  7878.                           (1 + user_command_array_len) *
  7879.                           sizeof (COMMAND *));
  7880.  
  7881.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  7882.   user_command_array[slot]->name = strdup (name);
  7883.   user_command_array[slot]->proc = proc;
  7884.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  7885. }
  7886.  
  7887. #if 0 /* unused */
  7888. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  7889. static void
  7890. define_alias (char *alias, char *function)
  7891. {
  7892. }
  7893. #endif
  7894.  
  7895. /* Set the paragraph indentation variable to the value specified in STRING.
  7896.    Values can be:
  7897.    `asis': Don't change existing indentation.
  7898.    `none': Remove existing indentation.
  7899.       NUM: Indent NUM spaces at the starts of paragraphs.
  7900.            Note that if NUM is zero, we assume `none'.
  7901.  
  7902.    Returns 0 if successful, or non-zero if STRING isn't one of the above. */
  7903. static int
  7904. set_paragraph_indent (char *string)
  7905. {
  7906.   if (strcmp (string, "asis") == 0)
  7907.     paragraph_start_indent = 0;
  7908.   else if (strcmp (string, "none") == 0)
  7909.     paragraph_start_indent = -1;
  7910.   else
  7911.     {
  7912.       if (sscanf (string, "%d", ¶graph_start_indent) != 1)
  7913.     return (-1);
  7914.       else
  7915.     {
  7916.       if (paragraph_start_indent == 0)
  7917.         paragraph_start_indent = -1;
  7918.     }
  7919.     }
  7920.   return (0);
  7921. }
  7922.  
  7923. static void
  7924. cm_paragraphindent (void)
  7925. {
  7926.   char *arg;
  7927.  
  7928.   get_rest_of_line (&arg);
  7929.   if (set_paragraph_indent (arg) != 0)
  7930.     line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
  7931.  
  7932.   free (arg);
  7933. }
  7934.  
  7935. /* Some support for footnotes. */
  7936.  
  7937. /* Footnotes are a new construct in Info.  We don't know the best method
  7938.    of implementing them for sure, so we present two possiblities.
  7939.  
  7940.    SeparateNode:
  7941.     Make them look like followed references, with the reference
  7942.     destinations in a makeinfo manufactured node or,
  7943.  
  7944.    EndNode:
  7945.     Make them appear at the bottom of the node that they originally
  7946.     appeared in. */
  7947. #define SeparateNode 0
  7948. #define EndNode 1
  7949.  
  7950. int footnote_style = EndNode;
  7951. int first_footnote_this_node = 1;
  7952. int footnote_count = 0;
  7953.  
  7954. /* Set the footnote style based on he style identifier in STRING. */
  7955. static int
  7956. set_footnote_style (char *string)
  7957. {
  7958.   if ((strcasecmp (string, "separate") == 0) ||
  7959.       (strcasecmp (string, "MN") == 0))
  7960.     footnote_style = SeparateNode;
  7961.   else if ((strcasecmp (string, "end") == 0) ||
  7962.        (strcasecmp (string, "EN") == 0))
  7963.     footnote_style = EndNode;
  7964.   else
  7965.     return (-1);
  7966.  
  7967.  return (0);
  7968. }
  7969.  
  7970. static void
  7971. cm_footnotestyle (void)
  7972. {
  7973.   char *arg;
  7974.  
  7975.   get_rest_of_line (&arg);
  7976.  
  7977.   if (set_footnote_style (arg) != 0)
  7978.     line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
  7979.  
  7980.   free (arg);
  7981. }
  7982.  
  7983. typedef struct fn
  7984. {
  7985.   struct fn *next;
  7986.   char *marker;
  7987.   char *note;
  7988. }  FN;
  7989.  
  7990. FN *pending_notes = (FN *) NULL;
  7991.  
  7992. /* A method for remembering footnotes.  Note that this list gets output
  7993.    at the end of the current node. */
  7994. static void
  7995. remember_note (char *marker, char *note)
  7996. {
  7997.   FN *temp = (FN *) xmalloc (sizeof (FN));
  7998.  
  7999.   temp->marker = strdup (marker);
  8000.   temp->note = strdup (note);
  8001.   temp->next = pending_notes;
  8002.   pending_notes = temp;
  8003.   footnote_count++;
  8004. }
  8005.  
  8006. /* How to get rid of existing footnotes. */
  8007. static void
  8008. free_pending_notes (void)
  8009. {
  8010.   FN *temp;
  8011.  
  8012.   while ((temp = pending_notes) != (FN *) NULL)
  8013.     {
  8014.       free (temp->marker);
  8015.       free (temp->note);
  8016.       pending_notes = pending_notes->next;
  8017.       free (temp);
  8018.     }
  8019.   first_footnote_this_node = 1;
  8020.   footnote_count = 0;
  8021. }
  8022.  
  8023. /* What to do when you see a @footnote construct. */
  8024.  
  8025.  /* Handle a "footnote".
  8026.     footnote *{this is a footnote}
  8027.     where "*" is the marker character for this note. */
  8028. static void
  8029. cm_footnote (void)
  8030. {
  8031.   char *marker;
  8032.   char *note;
  8033.  
  8034.   get_until ("{", &marker);
  8035.   canon_white (marker);
  8036.  
  8037.   /* Read the argument in braces. */
  8038.   if (curchar () != '{')
  8039.     {
  8040.       line_error ("`%c%s' expected more than just `%s'.  It needs something in `{...}'",
  8041.           COMMAND_PREFIX, command, marker);
  8042.       free (marker);
  8043.       return;
  8044.     }
  8045.   else
  8046.     {
  8047.       int braces = 1;
  8048.       int temp = ++input_text_offset;
  8049.       int len;
  8050.  
  8051.       while (braces)
  8052.     {
  8053.       if (temp == size_of_input_text)
  8054.         {
  8055.           line_error ("No closing brace for footnote `%s'", marker);
  8056.           return;
  8057.         }
  8058.  
  8059.       if (input_text[temp] == '{')
  8060.         braces++;
  8061.       else if (input_text[temp] == '}')
  8062.         braces--;
  8063.       else if (input_text[temp] == '\n')
  8064.         line_number ++;
  8065.  
  8066.       temp++;
  8067.     }
  8068.  
  8069.       len = (temp - input_text_offset) - 1;
  8070.       note = (char *)xmalloc (len + 1);
  8071.       strncpy (note, &input_text[input_text_offset], len);
  8072.       note[len] = '\0';
  8073.       input_text_offset = temp;
  8074.     }
  8075.  
  8076.   if (!current_node || !*current_node)
  8077.     {
  8078.       line_error ("Footnote defined without parent node");
  8079.       free (marker);
  8080.       free (note);
  8081.       return;
  8082.     }
  8083.  
  8084.   if (!*marker)
  8085.     {
  8086.       free (marker);
  8087.  
  8088.       if (number_footnotes)
  8089.     {
  8090.       marker = (char *)xmalloc (10);
  8091.       sprintf (marker, "%d", current_footnote_number);
  8092.       current_footnote_number++;
  8093.     }
  8094.       else
  8095.     marker = strdup ("*");
  8096.     }
  8097.  
  8098.   remember_note (marker, note);
  8099.  
  8100.   /* Your method should at least insert MARKER. */
  8101.   switch (footnote_style)
  8102.     {
  8103.     case SeparateNode:
  8104.       add_word_args ("(%s)", marker);
  8105.       if (first_footnote_this_node)
  8106.     {
  8107.       char *temp_string;
  8108.  
  8109.       temp_string = (char *)
  8110.         xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
  8111.  
  8112.       add_word_args (" (*note %s-Footnotes::)", current_node);
  8113.       strcpy (temp_string, current_node);
  8114.       strcat (temp_string, "-Footnotes");
  8115.       remember_node_reference (temp_string, line_number, followed_reference);
  8116.       free (temp_string);
  8117.       first_footnote_this_node = 0;
  8118.     }
  8119.       break;
  8120.  
  8121.     case EndNode:
  8122.       add_word_args ("(%s)", marker);
  8123.       break;
  8124.  
  8125.     default:
  8126.       break;
  8127.     }
  8128.   free (marker);
  8129.   free (note);
  8130. }
  8131.  
  8132. /* Non-zero means that we are currently in the process of outputting
  8133.    footnotes. */
  8134. int already_outputting_pending_notes = 0;
  8135.  
  8136. /* Output the footnotes.  We are at the end of the current node. */
  8137. static void
  8138. output_pending_notes (void)
  8139. {
  8140.   FN *footnote = pending_notes;
  8141.  
  8142.   if (!pending_notes)
  8143.     return;
  8144.  
  8145.   switch (footnote_style)
  8146.     {
  8147.  
  8148.     case SeparateNode:
  8149.       {
  8150.     char *old_current_node = current_node;
  8151.     char *old_command = strdup (command);
  8152.  
  8153.     already_outputting_pending_notes++;
  8154.     execute_string ("%cnode %s-Footnotes,,,%s\n",
  8155.             COMMAND_PREFIX, current_node, current_node);
  8156.     already_outputting_pending_notes--;
  8157.     current_node = old_current_node;
  8158.     free (command);
  8159.     command = old_command;
  8160.       }
  8161.       break;
  8162.  
  8163.     case EndNode:
  8164.       close_paragraph ();
  8165.       in_fixed_width_font++;
  8166.       execute_string ("---------- Footnotes ----------\n\n");
  8167.       in_fixed_width_font--;
  8168.       break;
  8169.     }
  8170.  
  8171.   /* Handle the footnotes in reverse order. */
  8172.   {
  8173.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  8174.  
  8175.     array[footnote_count] = (FN *) NULL;
  8176.  
  8177.     while (--footnote_count > -1)
  8178.       {
  8179.     array[footnote_count] = footnote;
  8180.     footnote = footnote->next;
  8181.       }
  8182.  
  8183.     filling_enabled = 1;
  8184.     indented_fill = 1;
  8185.  
  8186.     while ((footnote = array[++footnote_count]))
  8187.       {
  8188.  
  8189.     switch (footnote_style)
  8190.       {
  8191.       case SeparateNode:
  8192.       case EndNode:
  8193.         execute_string ("(%s)  %s", footnote->marker, footnote->note);
  8194.         close_paragraph ();
  8195.         break;
  8196.       }
  8197.       }
  8198.     close_paragraph ();
  8199.     free (array);
  8200.   }
  8201. }
  8202.  
  8203.  
  8204. /* **************************************************************** */
  8205. /*                                                                  */
  8206. /*              User definable Macros (text substitution)        */
  8207. /*                                                                  */
  8208. /* **************************************************************** */
  8209.  
  8210. #if defined (HAVE_MACROS)
  8211.  
  8212. /* Array of macros and definitions. */
  8213. MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
  8214.  
  8215. int macro_list_len = 0;        /* Number of elements. */
  8216. int macro_list_size = 0;    /* Number of slots in total. */
  8217.  
  8218. /* Return the macro definition of NAME or NULL if NAME is not defined. */
  8219. static MACRO_DEF *
  8220. find_macro (char *name)
  8221. {
  8222.   register int i;
  8223.   register MACRO_DEF *def;
  8224.  
  8225.   def = (MACRO_DEF *)NULL;
  8226.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  8227.     {
  8228.       if ((!def->inhibited) && (strcmp (def->name, name) == 0))
  8229.     break;
  8230.     }
  8231.   return (def);
  8232. }
  8233.  
  8234. /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
  8235.    SOURCE_FILE is the name of the file where this definition can be found,
  8236.    and SOURCE_LINENO is the line number within that file.  If a macro already
  8237.    exists with NAME, then a warning is produced, and that previous
  8238.    definition is overwritten. */
  8239. void
  8240. add_macro (char *name, char **arglist, char *body, char *source_file,
  8241.            int source_lineno, int flags)
  8242. {
  8243.   register MACRO_DEF *def;
  8244.  
  8245.   def = find_macro (name);
  8246.  
  8247.   if (!def)
  8248.     {
  8249.       if (macro_list_len + 2 >= macro_list_size)
  8250.     macro_list = (MACRO_DEF **)xrealloc
  8251.       (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
  8252.  
  8253.       macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
  8254.       macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
  8255.  
  8256.       def = macro_list[macro_list_len];
  8257.       macro_list_len += 1;
  8258.       def->name = name;
  8259.     }
  8260.   else
  8261.     {
  8262.       char *temp_filename = input_filename;
  8263.       int temp_line = line_number;
  8264.  
  8265.       warning ("The macro `%s' is previously defined", name);
  8266.  
  8267.       input_filename = def->source_file;
  8268.       line_number = def->source_lineno;
  8269.  
  8270.       warning ("Here is the previous definition of `%s'", name);
  8271.  
  8272.       input_filename = temp_filename;
  8273.       line_number = temp_line;
  8274.  
  8275.       if (def->arglist)
  8276.     {
  8277.       register int i;
  8278.  
  8279.       for (i = 0; def->arglist[i]; i++)
  8280.         free (def->arglist[i]);
  8281.  
  8282.       free (def->arglist);
  8283.     }
  8284.       free (def->source_file);
  8285.       free (def->body);
  8286.     }
  8287.  
  8288.   def->source_file = strdup (source_file);
  8289.   def->source_lineno = source_lineno;
  8290.   def->body = body;
  8291.   def->arglist = arglist;
  8292.   def->inhibited = 0;
  8293.   def->flags = flags;
  8294. }
  8295.  
  8296. /* Delete the macro with name NAME.  The macro is deleted from the list,
  8297.    but it is also returned.  If there was no macro defined, NULL is
  8298.    returned. */
  8299. static MACRO_DEF *
  8300. delete_macro (char *name)
  8301. {
  8302.   register int i;
  8303.   register MACRO_DEF *def;
  8304.  
  8305.   def = (MACRO_DEF *)NULL;
  8306.  
  8307.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  8308.     if (strcmp (def->name, name) == 0)
  8309.       {
  8310.     memcpy (macro_list + i, macro_list + i + 1,
  8311.            ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
  8312.     break;
  8313.       }
  8314.   return (def);
  8315. }
  8316.  
  8317. /* Return the arglist on the current line.  This can behave in two different
  8318.    ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
  8319. int braces_required_for_macro_args = 0;
  8320.  
  8321. static char **
  8322. get_macro_args (MACRO_DEF *def)
  8323. {
  8324.   register int i;
  8325.   char *word;
  8326.  
  8327.   /* Quickly check to see if this macro has been invoked with any arguments.
  8328.      If not, then don't skip any of the following whitespace. */
  8329.   for (i = input_text_offset; i < size_of_input_text; i++)
  8330.     if (!cr_or_whitespace (input_text[i]))
  8331.       break;
  8332.  
  8333.   if (input_text[i] != '{')
  8334.     {
  8335.       if (braces_required_for_macro_args)
  8336.     {
  8337.       return ((char **)NULL);
  8338.     }
  8339.       else
  8340.     {
  8341.       /* Braces are not required to fill out the macro arguments.  If
  8342.          this macro takes one argument, it is considered to be the
  8343.          remainder of the line, sans whitespace. */
  8344.       if (def->arglist && def->arglist[0] && !def->arglist[1])
  8345.         {
  8346.           char **arglist;
  8347.  
  8348.           get_rest_of_line (&word);
  8349.           input_text_offset--;
  8350.           canon_white (word);
  8351.           arglist = (char **)xmalloc (2 * sizeof (char *));
  8352.           arglist[0] = word;
  8353.           arglist[1] = (char *)NULL;
  8354.           return (arglist);
  8355.         }
  8356.       else
  8357.         {
  8358.           /* The macro either took no arguments, or took more than
  8359.          one argument.  In that case, it must be invoked with
  8360.          arguments surrounded by braces. */
  8361.           return ((char **)NULL);
  8362.         }
  8363.     }
  8364.     }
  8365.   return (get_brace_args (def->flags & ME_QUOTE_ARG));
  8366. }
  8367.  
  8368. /* Substitute actual parameters for named parameters in body.
  8369.    The named parameters which appear in BODY must by surrounded
  8370.    reverse slashes, as in \foo\. */
  8371. static char *
  8372. apply (char **named, char **actuals, char *body)
  8373. {
  8374.   register int i;
  8375.   int new_body_index, new_body_size;
  8376.   char *new_body, *text;
  8377.   int length_of_actuals;
  8378.  
  8379.   length_of_actuals = array_len (actuals);
  8380.   new_body_size = strlen (body);
  8381.   new_body = (char *)xmalloc (1 + new_body_size);
  8382.  
  8383.   /* Copy chars from BODY into NEW_BODY. */
  8384.   i = 0; new_body_index = 0;
  8385.  
  8386.   while (1)
  8387.     {
  8388.       if (!body[i])
  8389.     break;
  8390.  
  8391.       if (body[i] != '\\')
  8392.     new_body[new_body_index++] = body[i++];
  8393.       else
  8394.     {
  8395.       /* Snarf parameter name, check against named parameters. */
  8396.       char *param;
  8397.       int param_start, which, len;
  8398.  
  8399.       param_start = ++i;
  8400.       while ((body[i]) && (body[i] != '\\'))
  8401.         i++;
  8402.  
  8403.       len = i - param_start;
  8404.       param = (char *)xmalloc (1 + len);
  8405.       memcpy (param, body + param_start, len);
  8406.       param[len] = '\0';
  8407.  
  8408.       if (body[i])
  8409.         i++;
  8410.  
  8411.       /* Now check against named parameters. */
  8412.       for (which = 0; named && named[which]; which++)
  8413.         if (strcmp (named[which], param) == 0)
  8414.           break;
  8415.  
  8416.       if (named[which])
  8417.         {
  8418.           if (which < length_of_actuals)
  8419.         text = actuals[which];
  8420.           else
  8421.         text = (char *)NULL;
  8422.  
  8423.           if (!text)
  8424.         text = "";
  8425.  
  8426.           len = strlen (text);
  8427.         }
  8428.       else
  8429.         {
  8430.           len += 2;
  8431.           text = (char *)xmalloc (1 + len);
  8432.           sprintf (text, "\\%s\\", param);
  8433.         }
  8434.  
  8435.       if ((int) (2 + strlen (param)) < len)
  8436.         new_body = (char *)xrealloc
  8437.           (new_body, new_body_size += (1 + len));
  8438.  
  8439.       free (param);
  8440.  
  8441.       strcpy (new_body + new_body_index, text);
  8442.       new_body_index += len;
  8443.  
  8444.       if (!named[which])
  8445.         free (text);
  8446.     }
  8447.     }
  8448.   new_body[new_body_index] = '\0';
  8449.   return (new_body);
  8450. }
  8451.  
  8452. /* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
  8453. static void
  8454. execute_macro (MACRO_DEF *def)
  8455. {
  8456.   char **arglist;
  8457.   int num_args;
  8458.   char *execution_string = (char *)NULL;
  8459.  
  8460.   if (macro_expansion_output_stream && !me_inhibit_expansion)
  8461.     me_append_before_this_command ();
  8462.  
  8463.   /* Find out how many arguments this macro definition takes. */
  8464.   num_args = array_len (def->arglist);
  8465.  
  8466.   /* Gather the arguments present on the line if there are any. */
  8467.   arglist = get_macro_args (def);
  8468.  
  8469.   if (num_args < array_len (arglist))
  8470.     {
  8471.       free_array (arglist);
  8472.       line_error ("Macro `%s' called with too many args", def->name);
  8473.       return;
  8474.     }
  8475.  
  8476.   if (def->body)
  8477.     execution_string = apply (def->arglist, arglist, def->body);
  8478.  
  8479.   free_array (arglist);
  8480.  
  8481.   if (def->body)
  8482.     {
  8483.       if (macro_expansion_output_stream && !me_inhibit_expansion)
  8484.     {
  8485.       remember_itext (input_text, input_text_offset);
  8486.       me_execute_string (execution_string);
  8487.     }
  8488.       else
  8489.     execute_string (execution_string);
  8490.  
  8491.       free (execution_string);
  8492.     }
  8493. }
  8494.  
  8495. /* Read and remember the definition of a macro. */
  8496. static void
  8497. cm_macro (void)
  8498. {
  8499.   register int i;
  8500.   char *name, **arglist, *body, *line;
  8501.   int body_size, body_index;
  8502.   int depth = 1;
  8503.   int defining_line = line_number;
  8504.   int flags = 0;
  8505.  
  8506.   arglist = (char **)NULL;
  8507.   body = (char *)NULL;
  8508.   body_size = 0;
  8509.   body_index = 0;
  8510.  
  8511.   if (macro_expansion_output_stream)
  8512.     me_append_before_this_command ();
  8513.  
  8514.   skip_whitespace ();
  8515.  
  8516.   /* Get the name of the macro.  This is the set of characters which are
  8517.      not whitespace and are not `{' immediately following the @macro. */
  8518.   {
  8519.     int start = input_text_offset;
  8520.     int len;
  8521.  
  8522.     for (i = start;
  8523.      (i < size_of_input_text) &&
  8524.      (input_text[i] != '{') &&
  8525.      (!cr_or_whitespace (input_text[i]));
  8526.      i++);
  8527.  
  8528.     len = i - start;
  8529.     name = (char *)xmalloc (1 + len);
  8530.     strncpy (name, input_text + start, len);
  8531.     name[len] = '\0';
  8532.     input_text_offset = i;
  8533.   }
  8534.  
  8535.   skip_whitespace ();
  8536.  
  8537.   /* It is not required that the definition of a macro includes an arglist.
  8538.      If not, don't try to get the named parameters, just use a null list. */
  8539.   if (curchar () == '{')
  8540.     {
  8541.       int arglist_index = 0, arglist_size = 0;
  8542.       int gathering_words = 1;
  8543.       char *word = (char *)NULL;
  8544.       int character;
  8545.  
  8546.       /* Read the words inside of the braces which determine the arglist.
  8547.      These words will be replaced within the body of the macro at
  8548.      execution time. */
  8549.  
  8550.       input_text_offset++;
  8551.       skip_whitespace_and_newlines ();
  8552.  
  8553.       while (gathering_words)
  8554.     {
  8555.       int len;
  8556.  
  8557.       for (i = input_text_offset;
  8558.            (character = input_text[i]);
  8559.            i++)
  8560.         {
  8561.           switch (character)
  8562.         {
  8563.         case '\n':
  8564.           line_number++;
  8565.         case ' ':
  8566.         case '\t':
  8567.         case ',':
  8568.         case '}':
  8569.           /* Found the end of the current arglist word.  Save it. */
  8570.           len = i - input_text_offset;
  8571.           word = (char *)xmalloc (1 + len);
  8572.           strncpy (word, input_text + input_text_offset, len);
  8573.           word[len] = '\0';
  8574.           input_text_offset = i;
  8575.  
  8576.           /* Advance to the comma or close-brace that signified
  8577.              the end of the argument. */
  8578.           while ((character = curchar ())
  8579.              && character != ','
  8580.              && character != '}')
  8581.             input_text_offset++;
  8582.  
  8583.           /* Add the word to our list of words. */
  8584.           if ((arglist_index + 2) >= arglist_size)
  8585.             arglist = (char **)xrealloc
  8586.               (arglist, (arglist_size += 10) * sizeof (char *));
  8587.  
  8588.           arglist[arglist_index++] = word;
  8589.           arglist[arglist_index] = (char *)NULL;
  8590.           break;
  8591.         }
  8592.  
  8593.           if (character == '}')
  8594.         {
  8595.           input_text_offset++;
  8596.           gathering_words = 0;
  8597.           break;
  8598.         }
  8599.  
  8600.           if (character == ',')
  8601.         {
  8602.           input_text_offset++;
  8603.           skip_whitespace_and_newlines ();
  8604.           i = input_text_offset - 1;
  8605.         }
  8606.         }
  8607.     }
  8608.     }
  8609.  
  8610.   /* Read the text carefully until we find an "@end macro" which
  8611.      matches this one.  The text in between is the body of the macro. */
  8612.   skip_whitespace_and_newlines ();
  8613.  
  8614.   while (depth)
  8615.     {
  8616.       if ((input_text_offset + 9) > size_of_input_text)
  8617.     {
  8618.       int temp_line = line_number;
  8619.       line_number = defining_line;
  8620.       line_error ("%cend macro not found", COMMAND_PREFIX);
  8621.       line_number = temp_line;
  8622.       return;
  8623.     }
  8624.  
  8625.       get_rest_of_line (&line);
  8626.  
  8627.       /* Handle commands only meaningful within a macro. */
  8628.       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
  8629.       (strncmp (line + 1, "allow-recursion", 15) == 0) &&
  8630.       (line[16] == '\0' || whitespace (line[16])))
  8631.     {
  8632.       for (i = 16; whitespace (line[i]); i++);
  8633.       strcpy (line, line + i);
  8634.       flags |= ME_RECURSE;
  8635.       if (!*line)
  8636.         {
  8637.           free (line);
  8638.           continue;
  8639.         }
  8640.     }
  8641.  
  8642.       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
  8643.       (strncmp (line + 1, "quote-arg", 9) == 0) &&
  8644.       (line[10] == '\0' || whitespace (line[10])))
  8645.     {
  8646.       for (i = 16; whitespace (line[i]); i++);
  8647.       strcpy (line, line + i);
  8648.  
  8649.       if (arglist && arglist[0] && !arglist[1])
  8650.         {
  8651.           flags |= ME_QUOTE_ARG;
  8652.           if (!*line)
  8653.         {
  8654.           free (line);
  8655.           continue;
  8656.         }
  8657.         }
  8658.       else
  8659.         {
  8660.           line_error ("%cquote-arg only useful when the macro takes a single argument",
  8661.               COMMAND_PREFIX);
  8662.         }
  8663.     }
  8664.  
  8665.       if ((*line == COMMAND_PREFIX) &&
  8666.       (strncmp (line + 1, "macro ", 6) == 0))
  8667.     depth++;
  8668.  
  8669.       if ((*line == COMMAND_PREFIX) &&
  8670.       (strncmp (line + 1, "end macro", 9) == 0))
  8671.     depth--;
  8672.  
  8673.       if (depth)
  8674.     {
  8675.       if ((int) (body_index + strlen (line) + 3) >= body_size)
  8676.         body = (char *)xrealloc
  8677.           (body, body_size += 3 + strlen (line));
  8678.       strcpy (body + body_index, line);
  8679.       body_index += strlen (line);
  8680.       body[body_index++] = '\n';
  8681.       body[body_index] = '\0';
  8682.     }
  8683.       free (line);
  8684.     }
  8685.  
  8686.   /* We now have the name, the arglist, and the body.  However, BODY
  8687.      includes the final newline which preceded the `@end macro' text.
  8688.      Delete it. */
  8689.   if (body && strlen (body))
  8690.     body[strlen (body) - 1] = '\0';
  8691.  
  8692.   add_macro (name, arglist, body, input_filename, defining_line, flags);
  8693.  
  8694.   if (macro_expansion_output_stream)
  8695.     remember_itext (input_text, input_text_offset);
  8696. }
  8697.  
  8698. static void
  8699. cm_unmacro (void)
  8700. {
  8701.   register int i;
  8702.   char *line, *name;
  8703.   MACRO_DEF *def;
  8704.  
  8705.   if (macro_expansion_output_stream)
  8706.     me_append_before_this_command ();
  8707.  
  8708.   get_rest_of_line (&line);
  8709.   canon_white (line);
  8710.  
  8711.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  8712.   name = (char *)xmalloc (i);
  8713.   strncpy (name, line, i);
  8714.   name[i] = '\0';
  8715.  
  8716.   def = delete_macro (name);
  8717.  
  8718.   if (def)
  8719.     {
  8720.       free (def->source_file);
  8721.       free (def->name);
  8722.       free (def->body);
  8723.  
  8724.       if (def->arglist)
  8725.     {
  8726.       register int i;
  8727.  
  8728.       for (i = 0; def->arglist[i]; i++)
  8729.         free (def->arglist[i]);
  8730.  
  8731.       free (def->arglist);
  8732.     }
  8733.  
  8734.       free (def);
  8735.     }
  8736.  
  8737.   free (line);
  8738.   free (name);
  8739.  
  8740.   if (macro_expansion_output_stream)
  8741.     remember_itext (input_text, input_text_offset);
  8742. }
  8743.  
  8744. /* How to output sections of the input file verbatim. */
  8745.  
  8746. /* Set the value of POINTER's offset to OFFSET. */
  8747. static ITEXT *
  8748. remember_itext (char *pointer, int offset)
  8749. {
  8750.   register int i;
  8751.   ITEXT *itext = (ITEXT *)NULL;
  8752.  
  8753.   /* If we have no info, initialize a blank list. */
  8754.   if (!itext_info)
  8755.     {
  8756.       itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
  8757.       for (i = 0; i < itext_size; i++)
  8758.     itext_info[i] = (ITEXT *)NULL;
  8759.     }
  8760.  
  8761.   /* If the pointer is already present in the list, then set the offset. */
  8762.   for (i = 0; i < itext_size; i++)
  8763.     if ((itext_info[i] != (ITEXT *)NULL) &&
  8764.     (itext_info[i]->pointer == pointer))
  8765.       {
  8766.     itext = itext_info[i];
  8767.     itext_info[i]->offset = offset;
  8768.     break;
  8769.       }
  8770.  
  8771.   if (i == itext_size)
  8772.     {
  8773.       /* Find a blank slot, (or create a new one), and remember the
  8774.      pointer and offset. */
  8775.       for (i = 0; i < itext_size; i++)
  8776.     if (itext_info[i] == (ITEXT *)NULL)
  8777.       break;
  8778.  
  8779.       /* If not found, then add some slots. */
  8780.       if (i == itext_size)
  8781.     {
  8782.       register int j;
  8783.  
  8784.       itext_info = (ITEXT **)xrealloc
  8785.         (itext_info, (itext_size += 10) * sizeof (ITEXT *));
  8786.  
  8787.       for (j = i; j < itext_size; j++)
  8788.         itext_info[j] = (ITEXT *)NULL;
  8789.     }
  8790.  
  8791.       /* Now add the pointer and the offset. */
  8792.       itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
  8793.       itext_info[i]->pointer = pointer;
  8794.       itext_info[i]->offset = offset;
  8795.       itext = itext_info[i];
  8796.     }
  8797.   return (itext);
  8798. }
  8799.  
  8800. /* Forget the input text associated with POINTER. */
  8801. static void
  8802. forget_itext (char *pointer)
  8803. {
  8804.   register int i;
  8805.  
  8806.   for (i = 0; i < itext_size; i++)
  8807.     if (itext_info[i] && (itext_info[i]->pointer == pointer))
  8808.       {
  8809.     free (itext_info[i]);
  8810.     itext_info[i] = (ITEXT *)NULL;
  8811.     break;
  8812.       }
  8813. }
  8814.  
  8815. /* Append the text which appeared in input_text from the last offset to
  8816.    the character just before the command that we are currently executing. */
  8817. static void
  8818. me_append_before_this_command (void)
  8819. {
  8820.   register int i;
  8821.  
  8822.   for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
  8823.   maybe_write_itext (input_text, i);
  8824. }
  8825.  
  8826. /* Similar to execute_string (), but only takes a single string argument,
  8827.    and remembers the input text location, etc. */
  8828. static void
  8829. me_execute_string (char *execution_string)
  8830. {
  8831.   pushfile ();
  8832.   input_text_offset = 0;
  8833.   input_text = execution_string;
  8834.   input_filename = strdup (input_filename);
  8835.   size_of_input_text = strlen (execution_string);
  8836.  
  8837.   remember_itext (execution_string, 0);
  8838.  
  8839.   executing_string++;
  8840.   reader_loop ();
  8841.   popfile ();
  8842.   executing_string--;
  8843. }
  8844.  
  8845. /* Append the text which appears in input_text from the last offset to
  8846.    the current OFFSET. */
  8847. static void
  8848. append_to_expansion_output (int offset)
  8849. {
  8850.   register int i;
  8851.   ITEXT *itext = (ITEXT *)NULL;
  8852.  
  8853.   for (i = 0; i < itext_size; i++)
  8854.     if (itext_info[i] && itext_info[i]->pointer == input_text)
  8855.       {
  8856.     itext = itext_info[i];
  8857.     break;
  8858.       }
  8859.  
  8860.   if (!itext)
  8861.     itext = remember_itext (input_text, 0);
  8862.  
  8863.   if (offset > itext_info[i]->offset)
  8864.     {
  8865.       write_region_to_macro_output
  8866.     (input_text, itext_info[i]->offset, offset);
  8867.       remember_itext (input_text, offset);
  8868.     }
  8869. }
  8870.  
  8871. /* Only write this input text iff it appears in our itext list. */
  8872. static void
  8873. maybe_write_itext (char *pointer, int offset)
  8874. {
  8875.   register int i;
  8876.   ITEXT *itext = (ITEXT *)NULL;
  8877.  
  8878.   for (i = 0; i < itext_size; i++)
  8879.     if (itext_info[i] && (itext_info[i]->pointer == pointer))
  8880.       {
  8881.     itext = itext_info[i];
  8882.     break;
  8883.       }
  8884.  
  8885.   if (itext && (itext->offset < offset))
  8886.     {
  8887.       write_region_to_macro_output (itext->pointer, itext->offset, offset);
  8888.       remember_itext (pointer, offset);
  8889.     }
  8890. }
  8891.  
  8892. static void
  8893. write_region_to_macro_output (char *string, int start, int end)
  8894. {
  8895.   if (macro_expansion_output_stream)
  8896.     fwrite (string + start, 1, end - start, macro_expansion_output_stream);
  8897. }
  8898.  
  8899. #endif /* HAVE_MACROS */
  8900.  
  8901. /* Return the length of the array in ARRAY. */
  8902. static int
  8903. array_len (char **array)
  8904. {
  8905.   register int i = 0;
  8906.  
  8907.   if (array)
  8908.     for (i = 0; array[i] != (char *)NULL; i++);
  8909.  
  8910.   return (i);
  8911. }
  8912.  
  8913. static void
  8914. free_array (char **array)
  8915. {
  8916.   if (array)
  8917.     {
  8918.       register int i;
  8919.  
  8920.       for (i = 0; array[i] != (char *)NULL; i++)
  8921.     free (array[i]);
  8922.  
  8923.       free (array);
  8924.     }
  8925. }
  8926.  
  8927. /* Function is used even when we don't have macros.  Although, I have
  8928.    to admit, it is unlikely that you would have a use for it if you
  8929.    aren't using macros. */
  8930. static char **
  8931. get_brace_args (int quote_single)
  8932. {
  8933.   char **arglist, *word;
  8934.   int arglist_index, arglist_size;
  8935.   int character, escape_seen, start;
  8936.   int depth = 1;
  8937.  
  8938.   /* There is an arglist in braces here, so gather the args inside of it. */
  8939.   skip_whitespace_and_newlines ();
  8940.   input_text_offset++;
  8941.   arglist = (char **)NULL;
  8942.   arglist_index = arglist_size = 0;
  8943.  
  8944.  get_arg:
  8945.   skip_whitespace_and_newlines ();
  8946.   start = input_text_offset;
  8947.   escape_seen = 0;
  8948.  
  8949.   while ((character = curchar ()))
  8950.     {
  8951.       if (character == '\\')
  8952.     {
  8953.       input_text_offset += 2;
  8954.       escape_seen = 1;
  8955.     }
  8956.       else if (character == '{')
  8957.     {
  8958.       depth++;
  8959.       input_text_offset++;
  8960.     }
  8961.       else if ((character == ',' && !quote_single) ||
  8962.            ((character == '}') && depth == 1))
  8963.     {
  8964.       int len = input_text_offset - start;
  8965.  
  8966.       if (len || (character != '}'))
  8967.         {
  8968.           word = (char *)xmalloc (1 + len);
  8969.           strncpy (word, input_text + start, len);
  8970.           word[len] = '\0';
  8971.  
  8972.           /* Clean up escaped characters. */
  8973.           if (escape_seen)
  8974.         {
  8975.           register int i;
  8976.  
  8977.           for (i = 0; word[i]; i++)
  8978.             if (word[i] == '\\')
  8979.               memcpy (word + i, word + i + 1, strlen (word + i + 1));
  8980.         }
  8981.  
  8982.           if (arglist_index + 2 >= arglist_size)
  8983.         arglist = (char **)xrealloc
  8984.           (arglist, (arglist_size += 10) * sizeof (char *));
  8985.  
  8986.           arglist[arglist_index++] = word;
  8987.           arglist[arglist_index] = (char *)NULL;
  8988.         }
  8989.  
  8990.       input_text_offset++;
  8991.       if (character == '}')
  8992.         break;
  8993.       else
  8994.         goto get_arg;
  8995.     }
  8996.       else if (character == '}')
  8997.     {
  8998.       depth--;
  8999.       input_text_offset++;
  9000.     }
  9001.       else
  9002.     input_text_offset++;
  9003.     }
  9004.   return (arglist);
  9005. }
  9006.  
  9007. /* **************************************************************** */
  9008. /*                                                                  */
  9009. /*                  Looking For Include Files                       */
  9010. /*                                                                  */
  9011. /* **************************************************************** */
  9012.  
  9013. /* Given a string containing units of information separated by colons,
  9014.    return the next one pointed to by INDEX, or NULL if there are no more.
  9015.    Advance INDEX to the character after the colon. */
  9016. static char *
  9017. extract_colon_unit (char *string, int *index)
  9018. {
  9019.   int i, start;
  9020.  
  9021.   i = *index;
  9022.  
  9023.   if (!string || (i >= (int) strlen (string)))
  9024.     return ((char *)NULL);
  9025.  
  9026.   /* Each call to this routine leaves the index pointing at a colon if
  9027.      there is more to the path.  If I is > 0, then increment past the
  9028.      `:'.  If I is 0, then the path has a leading colon.  Trailing colons
  9029.      are handled OK by the `else' part of the if statement; an empty
  9030.      string is returned in that case. */
  9031.   if (i && string[i] == ':')
  9032.     i++;
  9033.  
  9034.   start = i;
  9035.  
  9036.   while (string[i] && string[i] != ':') i++;
  9037.  
  9038.   *index = i;
  9039.  
  9040.   if (i == start)
  9041.     {
  9042.       if (string[i])
  9043.     (*index)++;
  9044.  
  9045.       /* Return "" in the case of a trailing `:'. */
  9046.       return (strdup (""));
  9047.     }
  9048.   else
  9049.     {
  9050.       char *value;
  9051.  
  9052.       value = (char *)xmalloc (1 + (i - start));
  9053.       strncpy (value, &string[start], (i - start));
  9054.       value [i - start] = '\0';
  9055.  
  9056.       return (value);
  9057.     }
  9058. }
  9059.  
  9060. /* Return the full pathname for FILENAME by searching along PATH.
  9061.    When found, return the stat () info for FILENAME in FINFO.
  9062.    If PATH is NULL, only the current directory is searched.
  9063.    If the file could not be found, return a NULL pointer. */
  9064. static char *
  9065. get_file_info_in_path (char *filename, char *path, struct stat *finfo)
  9066. {
  9067.   char *dir;
  9068.   int result, index = 0;
  9069.  
  9070.   if (path == (char *)NULL)
  9071.     path = ".";
  9072.  
  9073.   /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
  9074.   if (*filename == '/' ||
  9075.       (*filename == '.' &&
  9076.        (filename[1] == '/' ||
  9077.     (filename[1] == '.' && filename[2] == '/'))))
  9078.     {
  9079.       if (stat (filename, finfo) == 0)
  9080.     return (strdup (filename));
  9081.       else
  9082.     return ((char *)NULL);
  9083.     }
  9084.  
  9085.   while ((dir = extract_colon_unit (path, &index)))
  9086.     {
  9087.       char *fullpath;
  9088.  
  9089.       if (!*dir)
  9090.     {
  9091.       free (dir);
  9092.       dir = strdup (".");
  9093.     }
  9094.  
  9095.       fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
  9096.       sprintf (fullpath, "%s/%s", dir, filename);
  9097.       free (dir);
  9098.  
  9099.       result = stat (fullpath, finfo);
  9100.  
  9101.       if (result == 0)
  9102.     return (fullpath);
  9103.       else
  9104.     free (fullpath);
  9105.     }
  9106.   return ((char *)NULL);
  9107. }
  9108.